@@ -152,6 +152,9 @@ static void dd_sidecar_on_reconnect(ddog_SidecarTransport *transport) {
152152
153153}
154154
155+ // Forward declaration for reconnection handler
156+ static ddog_SidecarTransport * dd_sidecar_connection_factory (void );
157+
155158static ddog_SidecarTransport * dd_sidecar_connection_factory_ex (bool is_fork ) {
156159 // Should not happen, unless the agent url is malformed
157160 if (!ddtrace_endpoint ) {
@@ -172,13 +175,18 @@ static ddog_SidecarTransport *dd_sidecar_connection_factory_ex(bool is_fork) {
172175 }
173176
174177 ddog_SidecarTransport * sidecar_transport ;
175- 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 ))) {
178+ // With threaded connectivity, use connect_worker to connect to master's listener
179+ if (!ddtrace_ffi_try ("Failed connecting to sidecar as worker" ,
180+ ddog_sidecar_connect_worker ((int32_t )ddtrace_master_pid , & sidecar_transport ))) {
176181 dd_free_endpoints ();
177182 return NULL ;
178183 }
179184
180185 dd_sidecar_post_connect (& sidecar_transport , is_fork , logpath );
181186
187+ // Set up automatic reconnection handler
188+ ddtrace_sidecar_reconnect (& sidecar_transport , dd_sidecar_connection_factory );
189+
182190 return sidecar_transport ;
183191}
184192
@@ -210,27 +218,153 @@ bool ddtrace_sidecar_maybe_enable_appsec(bool *appsec_activation, bool *appsec_c
210218#endif
211219}
212220
213- void ddtrace_sidecar_setup ( bool appsec_activation , bool appsec_config ) {
214- ddtrace_set_non_resettable_sidecar_globals ();
215- ddtrace_set_resettable_sidecar_globals ( );
221+ // Called in MINIT - stores master PID for threaded connectivity
222+ void ddtrace_sidecar_minit ( bool appsec_activation , bool appsec_config ) {
223+ UNUSED ( appsec_activation , appsec_config );
216224
217- // Store master PID for later fork detection
225+ // Store master PID for threaded connectivity only if not already set
226+ // This ensures that after fork, child processes keep the parent's PID
227+ if (ddtrace_master_pid == 0 ) {
228+ #ifdef _WIN32
229+ ddtrace_master_pid = _getpid ();
230+ #else
231+ ddtrace_master_pid = getpid ();
232+ #endif
233+ LOG (DEBUG , "Sidecar MINIT: Stored master PID=%d" , ddtrace_master_pid );
234+ } else {
235+ LOG (DEBUG , "Sidecar MINIT: Master PID already set to %d, current PID=%d" ,
236+ ddtrace_master_pid ,
237+ #ifdef _WIN32
238+ _getpid ()
239+ #else
240+ getpid ()
241+ #endif
242+ );
243+ }
244+ }
245+
246+ // Setup master sidecar connection - called once on first RINIT
247+ static void ddtrace_sidecar_setup_master (bool appsec_activation , bool appsec_config ) {
248+ // Check if we're in a forked child process
249+ // pthread_once state is not inherited across fork, so child processes
250+ // might try to call this function. They should NOT start a new master listener.
218251#ifndef _WIN32
219- ddtrace_master_pid = getpid ();
252+ ddtrace_pid_t current_pid = getpid ();
253+
254+ LOG (DEBUG , "ddtrace_sidecar_setup_master called: current_pid=%d, ddtrace_master_pid=%d" ,
255+ current_pid , ddtrace_master_pid );
256+
257+ bool is_child_process = (ddtrace_master_pid != 0 && current_pid != ddtrace_master_pid );
258+
259+ if (is_child_process ) {
260+ LOG (DEBUG , "Skipping master sidecar setup in child process (child_pid=%d, master_pid=%d)" ,
261+ current_pid , ddtrace_master_pid );
262+ // Child processes should only connect as workers, which is handled in dd_internal_handle_fork()
263+ return ;
264+ }
265+
266+ LOG (DEBUG , "Proceeding with master sidecar setup for PID=%d" , current_pid );
220267#endif
221268
269+ ddtrace_set_non_resettable_sidecar_globals ();
270+ ddtrace_set_resettable_sidecar_globals ();
271+
222272 ddog_init_remote_config (get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED (), appsec_activation , appsec_config );
223273
224- ddtrace_sidecar = dd_sidecar_connection_factory ();
225- if (!ddtrace_sidecar ) { // Something went wrong
274+ // Start master listener for worker processes/threads to connect to
275+ if (!ddtrace_ffi_try ("Failed starting sidecar master listener" , ddog_sidecar_connect_master ((int32_t )ddtrace_master_pid ))) {
276+ LOG (WARN , "Failed to start sidecar master listener" );
226277 if (ddtrace_endpoint ) {
227278 dd_free_endpoints ();
228279 }
280+ return ;
229281 }
230282
283+ // Master process also needs a transport - connect as first worker
284+ ddog_SidecarTransport * sidecar_transport = NULL ;
285+ if (!ddtrace_ffi_try ("Failed connecting master to sidecar" , ddog_sidecar_connect_worker ((int32_t )ddtrace_master_pid , & sidecar_transport ))) {
286+ LOG (WARN , "Failed to connect master process to sidecar" );
287+ if (ddtrace_endpoint ) {
288+ dd_free_endpoints ();
289+ }
290+ return ;
291+ }
292+
293+ char logpath [MAXPATHLEN ];
294+ int error_fd = atomic_load (& ddtrace_error_log_fd );
295+ if (error_fd == -1 || ddtrace_get_fd_path (error_fd , logpath ) < 0 ) {
296+ * logpath = 0 ;
297+ }
298+
299+ dd_sidecar_post_connect (& sidecar_transport , false, logpath );
300+ ddtrace_sidecar = sidecar_transport ;
301+
231302 if (get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED ()) {
232303 ddtrace_telemetry_first_init ();
233304 }
305+
306+ LOG (DEBUG , "Sidecar master setup complete, PID=%d" , ddtrace_master_pid );
307+ }
308+
309+ // Called in RINIT - handles git metadata and root span data
310+ void ddtrace_sidecar_rinit (void ) {
311+ if (get_DD_TRACE_GIT_METADATA_ENABLED ()) {
312+ zval git_object ;
313+ ZVAL_UNDEF (& git_object );
314+ ddtrace_inject_git_metadata (& git_object );
315+ if (Z_TYPE (git_object ) == IS_OBJECT ) {
316+ ddtrace_git_metadata * git_metadata = (ddtrace_git_metadata * ) Z_OBJ (git_object );
317+ if (Z_TYPE (git_metadata -> property_commit ) == IS_STRING ) {
318+ UNUSED (ddog_Vec_Tag_push (& DDTRACE_G (active_global_tags ), DDOG_CHARSLICE_C ("git.commit.sha" ),
319+ dd_zend_string_to_CharSlice (Z_STR (git_metadata -> property_commit ))));
320+ }
321+ if (Z_TYPE (git_metadata -> property_repository ) == IS_STRING ) {
322+ UNUSED (ddog_Vec_Tag_push (& DDTRACE_G (active_global_tags ), DDOG_CHARSLICE_C ("git.repository_url" ),
323+ dd_zend_string_to_CharSlice (Z_STR (git_metadata -> property_repository ))));
324+ }
325+ OBJ_RELEASE (& git_metadata -> std );
326+ }
327+ }
328+
329+ ddtrace_sidecar_submit_root_span_data_direct_defaults (& ddtrace_sidecar , NULL );
330+ }
331+
332+ // Setup function called via pthread_once on first RINIT
333+ void ddtrace_sidecar_setup (bool appsec_activation , bool appsec_config ) {
334+ // This is called on first RINIT when config is available
335+ // Set up the master sidecar connection with threaded connectivity
336+ ddtrace_sidecar_setup_master (appsec_activation , appsec_config );
337+ }
338+
339+ // Connect child process to master sidecar as worker after fork
340+ bool ddtrace_sidecar_connect_worker_after_fork (void ) {
341+ if (!ddtrace_endpoint || ddtrace_master_pid == 0 ) {
342+ return false;
343+ }
344+
345+ // Connect to master as worker using threaded connectivity
346+ ddog_SidecarTransport * sidecar_transport = NULL ;
347+ if (!ddtrace_ffi_try ("Failed connecting worker to sidecar after fork" ,
348+ ddog_sidecar_connect_worker ((int32_t )ddtrace_master_pid , & sidecar_transport ))) {
349+ return false;
350+ }
351+
352+ char logpath [MAXPATHLEN ];
353+ int error_fd = atomic_load (& ddtrace_error_log_fd );
354+ if (error_fd == -1 || ddtrace_get_fd_path (error_fd , logpath ) < 0 ) {
355+ * logpath = 0 ;
356+ }
357+
358+ // Post-connect with fork flag set to true
359+ dd_sidecar_post_connect (& sidecar_transport , true, logpath );
360+
361+ // Drop old transport and use the new worker connection
362+ if (ddtrace_sidecar ) {
363+ ddog_sidecar_transport_drop (ddtrace_sidecar );
364+ }
365+ ddtrace_sidecar = sidecar_transport ;
366+
367+ return true;
234368}
235369
236370void ddtrace_sidecar_ensure_active (void ) {
@@ -562,28 +696,6 @@ void ddtrace_sidecar_activate(void) {
562696 } ZEND_HASH_FOREACH_END ();
563697}
564698
565- void ddtrace_sidecar_rinit (void ) {
566- if (get_DD_TRACE_GIT_METADATA_ENABLED ()) {
567- zval git_object ;
568- ZVAL_UNDEF (& git_object );
569- ddtrace_inject_git_metadata (& git_object );
570- if (Z_TYPE (git_object ) == IS_OBJECT ) {
571- ddtrace_git_metadata * git_metadata = (ddtrace_git_metadata * ) Z_OBJ (git_object );
572- if (Z_TYPE (git_metadata -> property_commit ) == IS_STRING ) {
573- UNUSED (ddog_Vec_Tag_push (& DDTRACE_G (active_global_tags ), DDOG_CHARSLICE_C ("git.commit.sha" ),
574- dd_zend_string_to_CharSlice (Z_STR (git_metadata -> property_commit ))));
575- }
576- if (Z_TYPE (git_metadata -> property_repository ) == IS_STRING ) {
577- UNUSED (ddog_Vec_Tag_push (& DDTRACE_G (active_global_tags ), DDOG_CHARSLICE_C ("git.repository_url" ),
578- dd_zend_string_to_CharSlice (Z_STR (git_metadata -> property_repository ))));
579- }
580- OBJ_RELEASE (& git_metadata -> std );
581- }
582- }
583-
584- ddtrace_sidecar_submit_root_span_data_direct_defaults (& ddtrace_sidecar , NULL );
585- }
586-
587699void ddtrace_sidecar_rshutdown (void ) {
588700 ddog_Vec_Tag_drop (DDTRACE_G (active_global_tags ));
589701}
0 commit comments