void slave_stop_workers(Relay_log_info *rli, bool *mts_inited)
{
  int i;
  THD *thd= rli->info_thd;

  if (!*mts_inited) 
    return;
  else if (rli->slave_parallel_workers == 0)
    goto end;

  /*
    In case of the "soft" graceful stop Coordinator
    guaranteed Workers were assigned with full groups so waiting
    will be resultful.
    "Hard" stop with KILLing Coordinator or erroring out by a Worker
    can't wait for Workers' completion because those may not receive
    commit-events of last assigned groups.
  */
  if (rli->mts_group_status != Relay_log_info::MTS_KILLED_GROUP &&
      thd->killed == THD::NOT_KILLED)
  {
    DBUG_ASSERT(rli->mts_group_status != Relay_log_info::MTS_IN_GROUP ||
                thd->is_error());

#ifndef DBUG_OFF
    if (DBUG_EVALUATE_IF("check_slave_debug_group", 1, 0))
    {
      sql_print_error("This is not supposed to happen at this point...");
      DBUG_SUICIDE();
    }
#endif
    // No need to know a possible error out of synchronization call.
    (void)rli->current_mts_submode->wait_for_workers_to_finish(rli);
    /*
      At this point the coordinator has been stopped and the checkpoint
      routine is executed to eliminate possible gaps.
    */
    (void) mts_checkpoint_routine(rli, 0, false, true/*need_data_lock=true*/); // TODO: ALFRANIO ERROR
  }
  for (i= rli->workers.elements - 1; i >= 0; i--)
  {
    Slave_worker *w;
    get_dynamic((DYNAMIC_ARRAY*)&rli->workers, (uchar*) &w, i);
    
    mysql_mutex_lock(&w->jobs_lock);
    
    if (w->running_status != Slave_worker::RUNNING)
    {
      mysql_mutex_unlock(&w->jobs_lock);
      continue;
    }

    w->running_status= Slave_worker::KILLED;
    mysql_cond_signal(&w->jobs_cond);

    mysql_mutex_unlock(&w->jobs_lock);

    sql_print_information("Notifying Worker %lu to exit, thd %p", w->id,
                          w->info_thd);
  }

  thd_proc_info(thd, "Waiting for workers to exit");

  for (uint i= 0; i < rli->workers.elements; i++)
  {
    Slave_worker *w= NULL;
    get_dynamic((DYNAMIC_ARRAY*)&rli->workers, (uchar*) &w, i);

    /*
      Make copies for reporting through the performance schema tables.
      This is preserved until the next START SLAVE.
    */
    Slave_worker *worker_copy=new Slave_worker(NULL
    #ifdef HAVE_PSI_INTERFACE
                                               ,&key_relay_log_info_run_lock,
                                               &key_relay_log_info_data_lock,
                                               &key_relay_log_info_sleep_lock,
                                               &key_relay_log_info_thd_lock,
                                               &key_relay_log_info_data_cond,
                                               &key_relay_log_info_start_cond,
                                               &key_relay_log_info_stop_cond,
                                               &key_relay_log_info_sleep_cond
    #endif
                                               , 0);
    worker_copy->copy_values_for_PFS(w->id, w->running_status, w->info_thd,
                                     w->last_error(),
                                     w->currently_executing_gtid);
    rli->workers_copy_pfs.push_back(worker_copy);
  }

  for (i= rli->workers.elements - 1; i >= 0; i--)
  {
    Slave_worker *w= NULL;
    get_dynamic((DYNAMIC_ARRAY*)&rli->workers, (uchar*) &w, i);

    mysql_mutex_lock(&w->jobs_lock);
    while (w->running_status != Slave_worker::NOT_RUNNING)
    {
      PSI_stage_info old_stage;
      DBUG_ASSERT(w->running_status == Slave_worker::KILLED ||
                  w->running_status == Slave_worker::ERROR_LEAVING);

      thd->ENTER_COND(&w->jobs_cond, &w->jobs_lock,
                      &stage_slave_waiting_workers_to_exit, &old_stage);
      mysql_cond_wait(&w->jobs_cond, &w->jobs_lock);
      thd->EXIT_COND(&old_stage);
      mysql_mutex_lock(&w->jobs_lock);
    }
    mysql_mutex_unlock(&w->jobs_lock);
    // Free the current submode object
    delete w->current_mts_submode;
    w->current_mts_submode= 0;
    delete_dynamic_element(&rli->workers, i);
    delete w;
  }

  sql_print_information("Total MTS session statistics: "
                        "events processed = %llu; "
                        "worker queues filled over overrun level = %lu; "
                        "waited due a Worker queue full = %lu; "
                        "waited due the total size = %lu; "
                        "slept when Workers occupied = %lu ",
                        rli->mts_events_assigned, rli->mts_wq_overrun_cnt,
                        rli->mts_wq_overfill_cnt, rli->wq_size_waits_cnt,
                        rli->mts_wq_no_underrun_cnt);

  DBUG_ASSERT(rli->pending_jobs == 0);
  DBUG_ASSERT(rli->mts_pending_jobs_size == 0);

end:
  rli->mts_group_status= Relay_log_info::MTS_NOT_IN_GROUP;
  destroy_hash_workers(rli);
  delete rli->gaq;
  delete_dynamic(&rli->least_occupied_workers);    // least occupied

  // Destroy buffered events of the current group prior to exit.
  for (uint i= 0; i < rli->curr_group_da.elements; i++)
    delete *(Log_event**) dynamic_array_ptr(&rli->curr_group_da, i);
  delete_dynamic(&rli->curr_group_da);             // GCDA

  delete_dynamic(&rli->curr_group_assigned_parts); // GCAP
  rli->deinit_workers();
  rli->workers_array_initialized= false;
  rli->slave_parallel_workers= 0;
  free_root(&rli->mts_coor_mem_root, MYF(0));
  *mts_inited= false;
}