@@ -207,21 +207,19 @@ initialize_thread_for_net(EThread *thread)
207207}
208208
209209// Late-initialization continuation for ET_NET threads. Runs on each
210- // thread after start_HttpProxyServer() has created all listen sockets
211- // and all initialization FDs (eventfds, cache disks, DNS sockets, log
212- // files, plugin FDs) are in place .
210+ // thread after all initialization FDs (eventfds, cache disks, DNS
211+ // sockets, log files, plugin FDs) are in place but BEFORE
212+ // start_HttpProxyServer() schedules accept_per_thread .
213213//
214- // On Linux, when accept is performed directly on ET_NET threads
215- // (accept_threads == 0), this calls unshare(CLONE_FILES) to give each
216- // thread its own private kernel FD table, eliminating spinlock
217- // contention on accept4/close. The underlying kernel objects
218- // (struct file) are shared, so cross-thread eventfd signalling and
219- // shared cache-disk FDs keep working.
214+ // On Linux, when per-thread listen is enabled (exec_thread.listen=1,
215+ // SO_REUSEPORT), this calls unshare(CLONE_FILES) to give each thread
216+ // its own private kernel FD table, eliminating spinlock contention on
217+ // accept4/close. The subsequent do_listen() in accept_per_thread
218+ // creates the per-thread listen socket directly in the private table.
220219//
221- // This is NOT safe with dedicated accept threads (accept_threads > 0)
222- // because accepted sockets are created on the accept thread and handed
223- // off to ET_NET by FD number — after unshare those FDs would not exist
224- // in the ET_NET thread's private table.
220+ // The underlying kernel objects (struct file) are shared, so
221+ // cross-thread eventfd signalling and shared cache-disk FDs keep
222+ // working.
225223
226224namespace
227225{
@@ -230,13 +228,13 @@ struct ExecThrLateCont : public Continuation {
230228 mainEvent (int /* event ATS_UNUSED */ , Event * /* e ATS_UNUSED */ )
231229 {
232230#if defined(__linux__)
233- int accept_threads = RecGetRecordInt (" proxy.config.accept_threads " ).value_or (0 );
234- if (accept_threads > 0 ) {
235- Dbg (dbg_ctl_iocore_net, " ET_NET thread %d: skipping unshare (accept_threads=%d) " , this_ethread ()-> id , accept_threads);
236- } else if ( unshare (CLONE_FILES) < 0 ) {
237- Warning ( " ET_NET thread %d: unshare(CLONE_FILES) failed: %s " , this_ethread ()-> id , strerror (errno));
238- } else {
239- Dbg (dbg_ctl_iocore_net, " ET_NET thread %d: FD table unshared " , this_ethread ()-> id );
231+ int listen_per_thread = RecGetRecordInt (" proxy.config.exec_thread.listen " ).value_or (0 );
232+ if (listen_per_thread == 1 ) {
233+ if ( unshare (CLONE_FILES) < 0 ) {
234+ Dbg (dbg_ctl_iocore_net, " ET_NET thread %d: unshare(CLONE_FILES) failed: %s " , this_ethread ()-> id , strerror (errno));
235+ } else {
236+ Dbg (dbg_ctl_iocore_net, " ET_NET thread %d: FD table unshared " , this_ethread ()-> id );
237+ }
240238 }
241239#endif
242240 delete this ;
@@ -254,5 +252,5 @@ exec_thr_late_init()
254252 for (int i = 0 ; i < n; i++) {
255253 eventProcessor.thread_group [ET_NET]._thread [i]->schedule_imm (new ExecThrLateCont ());
256254 }
257- Note ( " Scheduled exec_thr_late_init() on %d ET_NET threads" , n);
255+ Dbg (dbg_ctl_iocore_net, " Scheduled exec_thr_late_init() on %d ET_NET threads" , n);
258256}
0 commit comments