2

I have to maintain an (old) Ada program that loads multiple DLLs (plugins) dynamically after its own start on Windows. Each DLL defines an entry point procedure that is called from the main program. When this entry point procedure is called each DLL may start several tasks (for example network communication). These tasks are active until the programm is stopped.

Unfortunately it happens sometimes that all tasks in one DLL get stuck but the main program keeps responsive (cyclic network communication/Text_IO). With the help of the tool Dr. Mingw I have extracted the call stacks after the tasks became unresponsive. A few tasks are waiting (for example for new data or delay statements) and that is okay but I have also noticed (in my opinion) strange call stacks (renamed packages and procedures).

Example 1 (good):

ntdll.dll!NtWaitForSingleObject
KERNELBASE.dll!WaitForSingleObject
foo_bar.dll!system__task_primitives__operations__cond_timed_wait
foo_bar.dll!system__task_primitives__operations__timed_sleep
foo_bar.dll!system__tasking__entry_calls__wait_for_completion_with_timeout
foo_bar.dll!system__tasking__protected_objects__operations__timed_protected_entry_call
foo_bar.dll!blob__foobar_task_typeTB  [C:/path/blob.adb @ 1481]
foo_bar.dll!system__tasking__stages__task_wrapper
KERNEL32.DLL!BaseThreadInitThunk
ntdll.dll!RtlGetAppContainerNamedObjectPath
ntdll.dll!RtlGetAppContainerNamedObjectPath
foo_bar.dll!system__tasking__stages__terminate_task

Example 2 (bad, seen multiple times for the function To_Unbounded_String in multiple tasks):

ntdll.dll!ZwWaitForAlertByThreadId
ntdll.dll!RtlDllShutdownInProgress
ntdll.dll!RtlDllShutdownInProgress
ntdll.dll!RtlEnterCriticalSection
ntdll.dll!RtlEnterCriticalSection
foo_bar.dll!system__tasking__initialization__task_lock__2
foo_bar.dll!__gnat_malloc
foo_bar.dll!ada__strings__unbounded__allocate.part.3
foo_bar.dll!ada__strings__unbounded__to_unbounded_string
foo_bar.dll!foo__bar__some_procedure  [C:/path/foo-bar.adb @ 1352]
foo_bar.dll!example__worker_task_typeTB  [C:/path/example.adb @ 1870]
foo_bar.dll!system__tasking__stages__task_wrapper
KERNEL32.DLL!BaseThreadInitThunk
ntdll.dll!RtlGetAppContainerNamedObjectPath
ntdll.dll!RtlGetAppContainerNamedObjectPath
foo_bar.dll!system__tasking__stages__terminate_task

Example 3 (bad):

ntdll.dll!ZwWaitForAlertByThreadId
ntdll.dll!RtlDllShutdownInProgress
ntdll.dll!RtlDllShutdownInProgress
ntdll.dll!RtlEnterCriticalSection
ntdll.dll!RtlEnterCriticalSection
foo_bar.dll!system__tasking__initialization__task_lock__2
foo_bar.dll!system__os_primitives__get_base_time
foo_bar.dll!ada__calendar__clock
foo_bar.dll!one__two__three__other_procedure  [C:/path/one-two-three.adb @ 139]
foo_bar.dll!example__worker_task_typeTB  [C:/path/example.adb @ 1487]
foo_bar.dll!system__tasking__stages__task_wrapper
KERNEL32.DLL!BaseThreadInitThunk
ntdll.dll!RtlGetAppContainerNamedObjectPath
ntdll.dll!RtlGetAppContainerNamedObjectPath
foo_bar.dll!system__tasking__stages__terminate_task

I am looking for possible advice on what could be causing the problem or how to analyze it further.

Marcello90
  • 1,313
  • 4
  • 18
  • 33

1 Answers1

1

Looking at current GCC sources (yours will be different) the global lock is taken here. I’m wondering whether the global lock in your dynamically-loaded DLL is the same as the one in your main program? Could it depend on how the DLLs are built (e.g. stand-alone, encapsulated)?

(later)

I built an encapsulated library which involves tasking, and sure enough System.Tasking.Global_Task_Lock is internal. Of course, this may not be a problem, though the word "Global" raises concerns.

This is with GCC 11.2.0 on macOS.

Simon Wright
  • 25,108
  • 2
  • 35
  • 62
  • What is the difference between a stand-alone and an encapsulated DLL? If I understand it correctly the `Global_Task_Lock` is a mutex and the lock has been already locked by some code part but is never unlocked (deadlock)? – Marcello90 Feb 26 '22 at 20:34
  • A stand-alone references other DLLs, e.g. the RTS, so the complete program should have only one reference to `libgnarl.dll`. An encapsulated one includes the whole RTS, so the main program has a different `libgnarl`. I can imagine this causing issues with e.g. protected objects visible and callable from the outside. But I’m not expert on this, just concerned. – Simon Wright Feb 27 '22 at 15:03