@@ -634,7 +634,7 @@ bool verify_client::finished(void)
634634// /////////////////////////////////////////////////////////////////////////
635635
636636client_group::client_group (benchmark_config* config, abstract_protocol *protocol, object_generator* obj_gen) :
637- m_base(NULL ), m_config(config), m_protocol(protocol), m_obj_gen(obj_gen)
637+ m_base(NULL ), m_config(config), m_protocol(protocol), m_obj_gen(obj_gen), m_stop_requested( false )
638638{
639639 m_base = event_base_new ();
640640 assert (m_base != NULL );
@@ -707,7 +707,25 @@ int client_group::prepare(void)
707707
708708void client_group::run (void )
709709{
710- event_base_dispatch (m_base);
710+ // Use EVLOOP_ONCE in a loop to periodically check the stop flag
711+ // This ensures we can exit promptly when force_stop() is called,
712+ // even if there are pending events (like slow responses)
713+ struct timeval timeout;
714+ timeout.tv_sec = 0 ;
715+ timeout.tv_usec = 100000 ; // 100ms timeout
716+
717+ while (!m_stop_requested.load (std::memory_order_relaxed)) {
718+ // Set a timeout so epoll_wait doesn't block indefinitely
719+ event_base_loopexit (m_base, &timeout);
720+
721+ // Run one iteration of the event loop
722+ int ret = event_base_loop (m_base, EVLOOP_ONCE);
723+
724+ // ret == 1 means no more events, ret == -1 means error
725+ if (ret != 0 ) {
726+ break ;
727+ }
728+ }
711729}
712730
713731void client_group::interrupt (void )
@@ -725,6 +743,9 @@ void client_group::force_stop(void)
725743 // Force stop all clients and break event loop for immediate shutdown
726744 // This is more aggressive than interrupt() - it forcefully cleans up all events
727745
746+ // Set the stop flag first - this will cause the run() loop to exit
747+ m_stop_requested.store (true , std::memory_order_relaxed);
748+
728749 // Mark all clients as interrupted
729750 set_all_clients_interrupted ();
730751
0 commit comments