@@ -153,6 +153,9 @@ static void dd_sidecar_on_reconnect(ddog_SidecarTransport *transport) {
153153
154154}
155155
156+ // Forward declaration for reconnection handler
157+ static ddog_SidecarTransport * dd_sidecar_connection_factory (void );
158+
156159static ddog_SidecarTransport * dd_sidecar_connection_factory_ex (bool is_fork ) {
157160 // Should not happen, unless the agent url is malformed
158161 if (!ddtrace_endpoint ) {
@@ -173,13 +176,18 @@ static ddog_SidecarTransport *dd_sidecar_connection_factory_ex(bool is_fork) {
173176 }
174177
175178 ddog_SidecarTransport * sidecar_transport ;
176- if (!ddtrace_ffi_try ("Failed connecting to the sidecar" , ddog_sidecar_connect_php (& sidecar_transport , logpath , dd_zend_string_to_CharSlice (get_global_DD_TRACE_LOG_LEVEL ()), get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED (), dd_sidecar_on_reconnect , ddtrace_endpoint ))) {
179+ // With threaded connectivity, use connect_worker to connect to master's listener
180+ if (!ddtrace_ffi_try ("Failed connecting to sidecar as worker" ,
181+ ddog_sidecar_connect_worker ((int32_t )ddtrace_master_pid , & sidecar_transport ))) {
177182 dd_free_endpoints ();
178183 return NULL ;
179184 }
180185
181186 dd_sidecar_post_connect (& sidecar_transport , is_fork , logpath );
182187
188+ // Set up automatic reconnection handler
189+ ddtrace_sidecar_reconnect (& sidecar_transport , dd_sidecar_connection_factory );
190+
183191 return sidecar_transport ;
184192}
185193
@@ -211,27 +219,153 @@ bool ddtrace_sidecar_maybe_enable_appsec(bool *appsec_activation, bool *appsec_c
211219#endif
212220}
213221
214- void ddtrace_sidecar_setup (bool appsec_activation , bool appsec_config ) {
215- ddtrace_set_non_resettable_sidecar_globals ();
216- ddtrace_set_resettable_sidecar_globals ();
222+ // Called in MINIT - stores master PID for threaded connectivity
223+ void ddtrace_sidecar_minit (bool appsec_activation , bool appsec_config ) {
224+ UNUSED (appsec_activation , appsec_config );
225+
226+ // Store master PID for threaded connectivity only if not already set
227+ // This ensures that after fork, child processes keep the parent's PID
228+ if (ddtrace_master_pid == 0 ) {
229+ #ifdef _WIN32
230+ ddtrace_master_pid = _getpid ();
231+ #else
232+ ddtrace_master_pid = getpid ();
233+ #endif
234+ LOG (DEBUG , "Sidecar MINIT: Stored master PID=%d" , ddtrace_master_pid );
235+ } else {
236+ LOG (DEBUG , "Sidecar MINIT: Master PID already set to %d, current PID=%d" ,
237+ ddtrace_master_pid ,
238+ #ifdef _WIN32
239+ _getpid ()
240+ #else
241+ getpid ()
242+ #endif
243+ );
244+ }
245+ }
217246
218- // Store master PID for later fork detection
247+ // Setup master sidecar connection - called once on first RINIT
248+ static void ddtrace_sidecar_setup_master (bool appsec_activation , bool appsec_config ) {
249+ // Check if we're in a forked child process
250+ // pthread_once state is not inherited across fork, so child processes
251+ // might try to call this function. They should NOT start a new master listener.
219252#ifndef _WIN32
220- ddtrace_master_pid = getpid ();
253+ ddtrace_pid_t current_pid = getpid ();
254+
255+ LOG (DEBUG , "ddtrace_sidecar_setup_master called: current_pid=%d, ddtrace_master_pid=%d" ,
256+ current_pid , ddtrace_master_pid );
257+
258+ bool is_child_process = (ddtrace_master_pid != 0 && current_pid != ddtrace_master_pid );
259+
260+ if (is_child_process ) {
261+ LOG (DEBUG , "Skipping master sidecar setup in child process (child_pid=%d, master_pid=%d)" ,
262+ current_pid , ddtrace_master_pid );
263+ // Child processes should only connect as workers, which is handled in dd_internal_handle_fork()
264+ return ;
265+ }
266+
267+ LOG (DEBUG , "Proceeding with master sidecar setup for PID=%d" , current_pid );
221268#endif
222269
270+ ddtrace_set_non_resettable_sidecar_globals ();
271+ ddtrace_set_resettable_sidecar_globals ();
272+
223273 ddog_init_remote_config (get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED (), appsec_activation , appsec_config );
224274
225- ddtrace_sidecar = dd_sidecar_connection_factory ();
226- if (!ddtrace_sidecar ) { // Something went wrong
275+ // Start master listener for worker processes/threads to connect to
276+ if (!ddtrace_ffi_try ("Failed starting sidecar master listener" , ddog_sidecar_connect_master ((int32_t )ddtrace_master_pid ))) {
277+ LOG (WARN , "Failed to start sidecar master listener" );
278+ if (ddtrace_endpoint ) {
279+ dd_free_endpoints ();
280+ }
281+ return ;
282+ }
283+
284+ // Master process also needs a transport - connect as first worker
285+ ddog_SidecarTransport * sidecar_transport = NULL ;
286+ if (!ddtrace_ffi_try ("Failed connecting master to sidecar" , ddog_sidecar_connect_worker ((int32_t )ddtrace_master_pid , & sidecar_transport ))) {
287+ LOG (WARN , "Failed to connect master process to sidecar" );
227288 if (ddtrace_endpoint ) {
228289 dd_free_endpoints ();
229290 }
291+ return ;
292+ }
293+
294+ char logpath [MAXPATHLEN ];
295+ int error_fd = atomic_load (& ddtrace_error_log_fd );
296+ if (error_fd == -1 || ddtrace_get_fd_path (error_fd , logpath ) < 0 ) {
297+ * logpath = 0 ;
230298 }
231299
300+ dd_sidecar_post_connect (& sidecar_transport , false, logpath );
301+ ddtrace_sidecar = sidecar_transport ;
302+
232303 if (get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED ()) {
233304 ddtrace_telemetry_first_init ();
234305 }
306+
307+ LOG (DEBUG , "Sidecar master setup complete, PID=%d" , ddtrace_master_pid );
308+ }
309+
310+ // Called in RINIT - handles git metadata and root span data
311+ void ddtrace_sidecar_rinit (void ) {
312+ if (get_DD_TRACE_GIT_METADATA_ENABLED ()) {
313+ zval git_object ;
314+ ZVAL_UNDEF (& git_object );
315+ ddtrace_inject_git_metadata (& git_object );
316+ if (Z_TYPE (git_object ) == IS_OBJECT ) {
317+ ddtrace_git_metadata * git_metadata = (ddtrace_git_metadata * ) Z_OBJ (git_object );
318+ if (Z_TYPE (git_metadata -> property_commit ) == IS_STRING ) {
319+ UNUSED (ddog_Vec_Tag_push (& DDTRACE_G (active_global_tags ), DDOG_CHARSLICE_C ("git.commit.sha" ),
320+ dd_zend_string_to_CharSlice (Z_STR (git_metadata -> property_commit ))));
321+ }
322+ if (Z_TYPE (git_metadata -> property_repository ) == IS_STRING ) {
323+ UNUSED (ddog_Vec_Tag_push (& DDTRACE_G (active_global_tags ), DDOG_CHARSLICE_C ("git.repository_url" ),
324+ dd_zend_string_to_CharSlice (Z_STR (git_metadata -> property_repository ))));
325+ }
326+ OBJ_RELEASE (& git_metadata -> std );
327+ }
328+ }
329+
330+ ddtrace_sidecar_submit_root_span_data_direct_defaults (& ddtrace_sidecar , NULL );
331+ }
332+
333+ // Setup function called via pthread_once on first RINIT
334+ void ddtrace_sidecar_setup (bool appsec_activation , bool appsec_config ) {
335+ // This is called on first RINIT when config is available
336+ // Set up the master sidecar connection with threaded connectivity
337+ ddtrace_sidecar_setup_master (appsec_activation , appsec_config );
338+ }
339+
340+ // Connect child process to master sidecar as worker after fork
341+ bool ddtrace_sidecar_connect_worker_after_fork (void ) {
342+ if (!ddtrace_endpoint || ddtrace_master_pid == 0 ) {
343+ return false;
344+ }
345+
346+ // Connect to master as worker using threaded connectivity
347+ ddog_SidecarTransport * sidecar_transport = NULL ;
348+ if (!ddtrace_ffi_try ("Failed connecting worker to sidecar after fork" ,
349+ ddog_sidecar_connect_worker ((int32_t )ddtrace_master_pid , & sidecar_transport ))) {
350+ return false;
351+ }
352+
353+ char logpath [MAXPATHLEN ];
354+ int error_fd = atomic_load (& ddtrace_error_log_fd );
355+ if (error_fd == -1 || ddtrace_get_fd_path (error_fd , logpath ) < 0 ) {
356+ * logpath = 0 ;
357+ }
358+
359+ // Post-connect with fork flag set to true
360+ dd_sidecar_post_connect (& sidecar_transport , true, logpath );
361+
362+ // Drop old transport and use the new worker connection
363+ if (ddtrace_sidecar ) {
364+ ddog_sidecar_transport_drop (ddtrace_sidecar );
365+ }
366+ ddtrace_sidecar = sidecar_transport ;
367+
368+ return true;
235369}
236370
237371void ddtrace_sidecar_ensure_active (void ) {
@@ -261,6 +395,20 @@ void ddtrace_sidecar_finalize(bool clear_id) {
261395}
262396
263397void ddtrace_sidecar_shutdown (void ) {
398+ // Shutdown master listener thread if this is the master process
399+ #ifndef _WIN32
400+ ddtrace_pid_t current_pid = getpid ();
401+ if (ddtrace_master_pid != 0 && current_pid == ddtrace_master_pid ) {
402+ LOG (DEBUG , "Shutting down master listener thread (PID=%d)" , current_pid );
403+ ddtrace_ffi_try ("Failed shutting down master listener" , ddog_sidecar_shutdown_master_listener ());
404+ }
405+ #else
406+ if (ddtrace_master_pid != 0 && _getpid () == ddtrace_master_pid ) {
407+ LOG (DEBUG , "Shutting down master listener thread (PID=%d)" , _getpid ());
408+ ddtrace_ffi_try ("Failed shutting down master listener" , ddog_sidecar_shutdown_master_listener ());
409+ }
410+ #endif
411+
264412 if (ddtrace_sidecar_instance_id ) {
265413 ddog_sidecar_instanceId_drop (ddtrace_sidecar_instance_id );
266414 }
@@ -563,28 +711,6 @@ void ddtrace_sidecar_activate(void) {
563711 } ZEND_HASH_FOREACH_END ();
564712}
565713
566- void ddtrace_sidecar_rinit (void ) {
567- if (get_DD_TRACE_GIT_METADATA_ENABLED ()) {
568- zval git_object ;
569- ZVAL_UNDEF (& git_object );
570- ddtrace_inject_git_metadata (& git_object );
571- if (Z_TYPE (git_object ) == IS_OBJECT ) {
572- ddtrace_git_metadata * git_metadata = (ddtrace_git_metadata * ) Z_OBJ (git_object );
573- if (Z_TYPE (git_metadata -> property_commit ) == IS_STRING ) {
574- UNUSED (ddog_Vec_Tag_push (& DDTRACE_G (active_global_tags ), DDOG_CHARSLICE_C ("git.commit.sha" ),
575- dd_zend_string_to_CharSlice (Z_STR (git_metadata -> property_commit ))));
576- }
577- if (Z_TYPE (git_metadata -> property_repository ) == IS_STRING ) {
578- UNUSED (ddog_Vec_Tag_push (& DDTRACE_G (active_global_tags ), DDOG_CHARSLICE_C ("git.repository_url" ),
579- dd_zend_string_to_CharSlice (Z_STR (git_metadata -> property_repository ))));
580- }
581- OBJ_RELEASE (& git_metadata -> std );
582- }
583- }
584-
585- ddtrace_sidecar_submit_root_span_data_direct_defaults (& ddtrace_sidecar , NULL );
586- }
587-
588714void ddtrace_sidecar_rshutdown (void ) {
589715 ddog_Vec_Tag_drop (DDTRACE_G (active_global_tags ));
590716}
0 commit comments