comp.lang.ada
 help / color / mirror / Atom feed
* How to kill GNAT DLL initialization?
@ 2016-08-10 12:51 Dmitry A. Kazakov
  2016-08-10 20:18 ` ahlan.marriott
  0 siblings, 1 reply; 7+ messages in thread
From: Dmitry A. Kazakov @ 2016-08-10 12:51 UTC (permalink / raw)


As it seems GNAT DLL may not have initialization code invoked in 
DllMain. This inevitable deadlocks RTS. Removing user library-level 
tasks does not help. The RTS itself contains library-level tasks. E.g. 
when asynchronous select is used.

Now the question is how to kill calls to initialization code from DllMain.

    for Library_Auto_Init use "false";

Is not enough. Are there other attributes I missed?

(Maybe there is a way to remove DllMain from the external symbols list 
during linking?)

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: How to kill GNAT DLL initialization?
  2016-08-10 12:51 How to kill GNAT DLL initialization? Dmitry A. Kazakov
@ 2016-08-10 20:18 ` ahlan.marriott
  2016-08-11  7:31   ` Dmitry A. Kazakov
  0 siblings, 1 reply; 7+ messages in thread
From: ahlan.marriott @ 2016-08-10 20:18 UTC (permalink / raw)


On Wednesday, 10 August 2016 14:52:33 UTC+2, Dmitry A. Kazakov  wrote:
> As it seems GNAT DLL may not have initialization code invoked in 
> DllMain. This inevitable deadlocks RTS. Removing user library-level 
> tasks does not help. The RTS itself contains library-level tasks. E.g. 
> when asynchronous select is used.
> 
> Now the question is how to kill calls to initialization code from DllMain.
> 
>     for Library_Auto_Init use "false";
> 
> Is not enough. Are there other attributes I missed?
> 
> (Maybe there is a way to remove DllMain from the external symbols list 
> during linking?)
> 
> -- 
> Regards,
> Dmitry A. Kazakov
> http://www.dmitry-kazakov.de

Dear Dmitry,

We produce DLLs written using GNAT that are used by other languages - typically C++
We don't explicitly run any initialisation code, the main procedure is always empty.
(although this is probably a left over from our ObjectAda days)
If something has to be initialised then we put this into a procedure, export it and request that our C++ user calls this before calling any other routine.

Here is a typical Gpr file for one of our Dlls.

-------
project Monitor is

   package Naming is
      for Casing use "mixedcase";
   end Naming;

   for Library_Name use "Monitor";
   for Shared_Library_Prefix use "";

   for Source_Dirs use ("W:\Source\Ada\Interfaces\Monitor",
                        "W:\Source\Ada\Interfaces",
                        "W:\Source\Ada\Shared",
                        "W:\Source\Ada\Open\Shared",
                        "W:\Source\Ada\Open\Shared\Windows");

   for Library_Interface use ("Monitor_Interface");

   for Object_Dir use "objects";

   for Library_Options use ("-LW:\Product\Windows", "resources.o");
   for Library_Dir use "W:\Product\Windows";
   for Library_Ali_Dir use "D:\Binary\Ada\Interfaces\Monitor";
   for Library_Kind use "dynamic";
   for Library_Standalone use "encapsulated";

   package Pretty_Printer is
      for Default_Switches ("ada") use ("-i2", "-M120", "-aL", "-A1", "-A4");
   end Pretty_Printer;

   package Builder is
      for Default_Switches ("ada") use ("-s", "-g");
   end Builder;

   package Compiler is
      for Default_Switches ("ada") use
         ("-O1", "-gnatQ", "-gnata", "-gnato", "-g", "-gnat12",
          "-gnatwcehijkmopruvz.c.n.p.t.w.x", "-gnatykmpM120");
   end Compiler;

   package Binder is
      for Default_Switches ("ada") use ("-E");
   end Binder;

end Monitor;

-------

and the main package.

-------
with Monitor_Interface; --> UD: Drag in Code

procedure Monitor is
begin
  null;
end Monitor;
-------

Monitor_Interface ads exports all the procedures that the DLL provides and the Adb implements them.

Hoping this helps,
MfG
Ahlan

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: How to kill GNAT DLL initialization?
  2016-08-10 20:18 ` ahlan.marriott
@ 2016-08-11  7:31   ` Dmitry A. Kazakov
  2016-08-12  7:56     ` ahlan.marriott
  0 siblings, 1 reply; 7+ messages in thread
From: Dmitry A. Kazakov @ 2016-08-11  7:31 UTC (permalink / raw)


On 10/08/2016 22:18, ahlan.marriott@gmail.com wrote:
> On Wednesday, 10 August 2016 14:52:33 UTC+2, Dmitry A. Kazakov  wrote:
>> As it seems GNAT DLL may not have initialization code invoked in
>> DllMain. This inevitable deadlocks RTS. Removing user library-level
>> tasks does not help. The RTS itself contains library-level tasks. E.g.
>> when asynchronous select is used.
>>
>> Now the question is how to kill calls to initialization code from DllMain.
>>
>>     for Library_Auto_Init use "false";
>>
>> Is not enough. Are there other attributes I missed?
>>
>> (Maybe there is a way to remove DllMain from the external symbols list
>> during linking?)
>>
> We produce DLLs written using GNAT that are used by other languages - typically C++
> We don't explicitly run any initialisation code, the main procedure is always empty.
> (although this is probably a left over from our ObjectAda days)
> If something has to be initialised then we put this into a
> procedure,  export it and request that our C++ user calls this before calling any
> other routine.

Yes, that is my plan too. GNAT generates files b__dllname.ads/adb 
containing an initialization procedure dllnameinit. This must be called 
to initialize the RTS. The problem is that it is called automatically 
upon DLL load on the context where it freezes. I must find a way to kill 
that call.

> Here is a typical Gpr file for one of our Dlls.
>
> -------
> project Monitor is
>
>    package Naming is
>       for Casing use "mixedcase";
>    end Naming;
>
>    for Library_Name use "Monitor";
>    for Shared_Library_Prefix use "";
>
>    for Source_Dirs use ("W:\Source\Ada\Interfaces\Monitor",
>                         "W:\Source\Ada\Interfaces",
>                         "W:\Source\Ada\Shared",
>                         "W:\Source\Ada\Open\Shared",
>                         "W:\Source\Ada\Open\Shared\Windows");
>
>    for Library_Interface use ("Monitor_Interface");
>
>    for Object_Dir use "objects";
>
>    for Library_Options use ("-LW:\Product\Windows", "resources.o");
>    for Library_Dir use "W:\Product\Windows";
>    for Library_Ali_Dir use "D:\Binary\Ada\Interfaces\Monitor";
>    for Library_Kind use "dynamic";
>    for Library_Standalone use "encapsulated";
>
>    package Pretty_Printer is
>       for Default_Switches ("ada") use ("-i2", "-M120", "-aL", "-A1", "-A4");
>    end Pretty_Printer;
>
>    package Builder is
>       for Default_Switches ("ada") use ("-s", "-g");
>    end Builder;
>
>    package Compiler is
>       for Default_Switches ("ada") use
>          ("-O1", "-gnatQ", "-gnata", "-gnato", "-g", "-gnat12",
>           "-gnatwcehijkmopruvz.c.n.p.t.w.x", "-gnatykmpM120");
>    end Compiler;
>
>    package Binder is
>       for Default_Switches ("ada") use ("-E");
>    end Binder;
>
> end Monitor;

My gpr project is no different, except that Library_Interface is a 
package with some procedures.

> -------
>
> and the main package.
>
> -------
> with Monitor_Interface; --> UD: Drag in Code
>
> procedure Monitor is
> begin
>   null;
> end Monitor;
> -------
>
> Monitor_Interface ads exports all the procedures that the DLL
> provides  and the Adb implements them.

I found that my version of GNAT puts the DLL initialization program into 
the .ctor section. That causes it called regardless anything.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: How to kill GNAT DLL initialization?
  2016-08-11  7:31   ` Dmitry A. Kazakov
@ 2016-08-12  7:56     ` ahlan.marriott
  2016-08-12 10:10       ` Dmitry A. Kazakov
  0 siblings, 1 reply; 7+ messages in thread
From: ahlan.marriott @ 2016-08-12  7:56 UTC (permalink / raw)


On Thursday, 11 August 2016 09:32:26 UTC+2, Dmitry A. Kazakov  wrote:
> On 10/08/2016 22:18, ahlan.marriott@gmail.com wrote:
> > On Wednesday, 10 August 2016 14:52:33 UTC+2, Dmitry A. Kazakov  wrote:
> >> As it seems GNAT DLL may not have initialization code invoked in
> >> DllMain. This inevitable deadlocks RTS. Removing user library-level
> >> tasks does not help. The RTS itself contains library-level tasks. E.g.
> >> when asynchronous select is used.
> >>
> >> Now the question is how to kill calls to initialization code from DllMain.
> >>
> >>     for Library_Auto_Init use "false";
> >>
> >> Is not enough. Are there other attributes I missed?
> >>
> >> (Maybe there is a way to remove DllMain from the external symbols list
> >> during linking?)
> >>
> > We produce DLLs written using GNAT that are used by other languages - typically C++
> > We don't explicitly run any initialisation code, the main procedure is always empty.
> > (although this is probably a left over from our ObjectAda days)
> > If something has to be initialised then we put this into a
> > procedure,  export it and request that our C++ user calls this before calling any
> > other routine.
> 
> Yes, that is my plan too. GNAT generates files b__dllname.ads/adb 
> containing an initialization procedure dllnameinit. This must be called 
> to initialize the RTS. The problem is that it is called automatically 
> upon DLL load on the context where it freezes. I must find a way to kill 
> that call.
> 
> > Here is a typical Gpr file for one of our Dlls.
> >
> > -------
> > project Monitor is
> >
> >    package Naming is
> >       for Casing use "mixedcase";
> >    end Naming;
> >
> >    for Library_Name use "Monitor";
> >    for Shared_Library_Prefix use "";
> >
> >    for Source_Dirs use ("W:\Source\Ada\Interfaces\Monitor",
> >                         "W:\Source\Ada\Interfaces",
> >                         "W:\Source\Ada\Shared",
> >                         "W:\Source\Ada\Open\Shared",
> >                         "W:\Source\Ada\Open\Shared\Windows");
> >
> >    for Library_Interface use ("Monitor_Interface");
> >
> >    for Object_Dir use "objects";
> >
> >    for Library_Options use ("-LW:\Product\Windows", "resources.o");
> >    for Library_Dir use "W:\Product\Windows";
> >    for Library_Ali_Dir use "D:\Binary\Ada\Interfaces\Monitor";
> >    for Library_Kind use "dynamic";
> >    for Library_Standalone use "encapsulated";
> >
> >    package Pretty_Printer is
> >       for Default_Switches ("ada") use ("-i2", "-M120", "-aL", "-A1", "-A4");
> >    end Pretty_Printer;
> >
> >    package Builder is
> >       for Default_Switches ("ada") use ("-s", "-g");
> >    end Builder;
> >
> >    package Compiler is
> >       for Default_Switches ("ada") use
> >          ("-O1", "-gnatQ", "-gnata", "-gnato", "-g", "-gnat12",
> >           "-gnatwcehijkmopruvz.c.n.p.t.w.x", "-gnatykmpM120");
> >    end Compiler;
> >
> >    package Binder is
> >       for Default_Switches ("ada") use ("-E");
> >    end Binder;
> >
> > end Monitor;
> 
> My gpr project is no different, except that Library_Interface is a 
> package with some procedures.
> 
> > -------
> >
> > and the main package.
> >
> > -------
> > with Monitor_Interface; --> UD: Drag in Code
> >
> > procedure Monitor is
> > begin
> >   null;
> > end Monitor;
> > -------
> >
> > Monitor_Interface ads exports all the procedures that the DLL
> > provides  and the Adb implements them.
> 
> I found that my version of GNAT puts the DLL initialization program into 
> the .ctor section. That causes it called regardless anything.
> 
> -- 
> Regards,
> Dmitry A. Kazakov
> http://www.dmitry-kazakov.de

Dear Dmitry,
I can't help but feel that you are going about this the wrong way.
I don't think you should be trying to prevent initialisation.
If you succeed then your DLL probably won't work because things need to be initialised.
We do all our PC programming in Ada.
Unfortunately not everyone is so enlightened and so we have to produce DLLs for other departments to use.
Consequently we have over the years produced a good many Windows DLLs that are used by a variety of languages, including Microsoft C++ and even Ada.
(We do in fact have a problem when our DLLs are called by an Ada program that uses Gtk later than 3.8.2 running on Windows XP - however I suspect that this isn't the problem you are experiencing)
Although our main is empty this is just because our main package has no code - the other packages have normal initialisation code and of course are elaborated.
The three things we need to be careful with are:
1) The interface has to use the C calling convention - including callbacks.
2) We don't attempt to propagate exceptions. Our interfaces are all functions that return a return code. Exceptions in the DLL are caught in the interface procedures and converted into an error code that is passed back to the caller.
3) Tasks must not be created either in package bodies nor in their elaboration. My guess is this is where your problem really lies. You can't have static tasks, only dynamic tasks and these must not be created in the package body - i.e. as part of the package elaboration or between the package begin and end.

The task restriction is normally where we trip over. So I suggest that you check very carefully where your tasks are being created. They can only be created as a result of being called by the DLL user not automatically by the DLL. 

This is why most of our DLLs provide a function called Initialise that we require the DLL user to call before using any other of the provided functions. We also usually have a Finalise procedure that the DLL user must call so that we can terminate all our tasks - otherwise the main program won't exit.

Really, creating DLLs using a recent version (>=GPL;2014) of GNAT isn't a problem.
It is a bit more complicated if you are using a older version of GNAT because GNAT did not automatically make the transitive closure and on really old versions of GNAT you had to manually cause the DLL packages to be elaborated using AdaInit - but that is another story...

Hoping this helps,
MfG
Ahlan

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: How to kill GNAT DLL initialization?
  2016-08-12  7:56     ` ahlan.marriott
@ 2016-08-12 10:10       ` Dmitry A. Kazakov
  2016-08-13  8:59         ` ahlan.marriott
  0 siblings, 1 reply; 7+ messages in thread
From: Dmitry A. Kazakov @ 2016-08-12 10:10 UTC (permalink / raw)


On 12/08/2016 09:56, ahlan.marriott@gmail.com wrote:

> I can't help but feel that you are going about this the wrong way.

I found the reason, in the project file the "-a" switch was specified 
explicitly for default Binder switches. That overrode the effect of

    for Library_Auto_Init use "false";

> I don't think you should be trying to prevent initialisation.

That is the only way because my DLL uses tasking.

> If you succeed then your DLL probably won't work because things need to be initialised.

My DLL interface calls to initialization from all exported operation.

> We do all our PC programming in Ada.
> Unfortunately not everyone is so enlightened and so we have to produce DLLs for other departments to use.

There are other reasons to use encapsulated libraries. GNU linker 
performance becomes catastrophic with the number of libraries we have. 
But I would prefer an ability to have accumulating library projects that 
will produce single static or dynamic library out of multiple libraries. 
Encapsulated GNAT run-time is not an issue to me.

> The task restriction is normally where we trip over. So I suggest
> that  you check very carefully where your tasks are being created. They can
> only be created as a result of being called by the DLL user not
> automatically by the DLL.

That is a bug on GNAT's side, IMO. The problem is not only tasks but 
also asynchronous select. If you have any, RTS will create an internal 
task during initialization and so freeze. Possibly there are other cases 
when Lock_RTS is called. It is Lock_RTS that causes freezing.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: How to kill GNAT DLL initialization?
  2016-08-12 10:10       ` Dmitry A. Kazakov
@ 2016-08-13  8:59         ` ahlan.marriott
  2016-08-13 19:05           ` Dmitry A. Kazakov
  0 siblings, 1 reply; 7+ messages in thread
From: ahlan.marriott @ 2016-08-13  8:59 UTC (permalink / raw)


On Friday, 12 August 2016 12:11:30 UTC+2, Dmitry A. Kazakov  wrote:
> On 12/08/2016 09:56, ahlan.marriott@gmail.com wrote:
> 
> > I can't help but feel that you are going about this the wrong way.
> 
> I found the reason, in the project file the "-a" switch was specified 
> explicitly for default Binder switches. That overrode the effect of
> 
>     for Library_Auto_Init use "false";
> 
> > I don't think you should be trying to prevent initialisation.
> 
> That is the only way because my DLL uses tasking.
> 
> > If you succeed then your DLL probably won't work because things need to be initialised.
> 
> My DLL interface calls to initialization from all exported operation.
> 
> > We do all our PC programming in Ada.
> > Unfortunately not everyone is so enlightened and so we have to produce DLLs for other departments to use.
> 
> There are other reasons to use encapsulated libraries. GNU linker 
> performance becomes catastrophic with the number of libraries we have. 
> But I would prefer an ability to have accumulating library projects that 
> will produce single static or dynamic library out of multiple libraries. 
> Encapsulated GNAT run-time is not an issue to me.
> 
> > The task restriction is normally where we trip over. So I suggest
> > that  you check very carefully where your tasks are being created. They can
> > only be created as a result of being called by the DLL user not
> > automatically by the DLL.
> 
> That is a bug on GNAT's side, IMO. The problem is not only tasks but 
> also asynchronous select. If you have any, RTS will create an internal 
> task during initialization and so freeze. Possibly there are other cases 
> when Lock_RTS is called. It is Lock_RTS that causes freezing.
> 
> -- 
> Regards,
> Dmitry A. Kazakov
> http://www.dmitry-kazakov.de

Dear Dmitry,
I am glad that you managed to solve your problem.
Although I wonder what you are doing that requires you to disable the library auto init.
We don't for any of our DLLs almost all of which create and use tasks although not from the dll_Main nor during elaboration.

Bes wishes,
Ahlan

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: How to kill GNAT DLL initialization?
  2016-08-13  8:59         ` ahlan.marriott
@ 2016-08-13 19:05           ` Dmitry A. Kazakov
  0 siblings, 0 replies; 7+ messages in thread
From: Dmitry A. Kazakov @ 2016-08-13 19:05 UTC (permalink / raw)


On 2016-08-13 10:59, ahlan.marriott@gmail.com wrote:
> Although I wonder what you are doing that requires you to disable the
> library auto init.

Whatever that calls Lock_RTS during initialization.

> We don't for any of our DLLs almost all of which create and use
> tasks  although not from the dll_Main nor during elaboration.

As I said, it is required but not sufficient. It would be nice if 
AdaCore provided a full list. If you want to know, you must scan all RTS 
files for calls to Lock_RTS and see if any of the subprograms doing this 
is called from b__libnameinit.

Even better, they should make Windows gnatbind fail when the 
initialization procedure it generates will freeze. It is clearly a bug 
to me.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2016-08-13 19:05 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-08-10 12:51 How to kill GNAT DLL initialization? Dmitry A. Kazakov
2016-08-10 20:18 ` ahlan.marriott
2016-08-11  7:31   ` Dmitry A. Kazakov
2016-08-12  7:56     ` ahlan.marriott
2016-08-12 10:10       ` Dmitry A. Kazakov
2016-08-13  8:59         ` ahlan.marriott
2016-08-13 19:05           ` Dmitry A. Kazakov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox