[m-dev.] Initialising the Mercury runtime in .NET

Peter Ross pro at missioncriticalit.com
Tue Oct 14 01:28:29 AEST 2003

On Tue, Oct 14, 2003 at 12:31:27AM +1000, Fergus Henderson wrote:
> On 13-Oct-2003, Peter Ross <pro at missioncriticalit.com> wrote:
> > Before one can use Mercury.NET, one must call io__init_state.
> >
> > Currently this is done by inserting the call to MR_init_runtime, which
> > simply calls io__init_state but may at a later date do more, in the
> > class constructor (.cctor) of the assembly containing main/2.  This is
> > problematic if you want to use a Mercury.NET assembly as a library as
> > then this routine isn't called.
> It is problematic if you don't call it, yes.
> There is a fairly straight-forward solution, however: call it.
Yes, but the problem is that you only want to call it once because you
don't want to be re-initialising the stream map and the globals,
everytime you re-enter the library.

Of course we can add a new piece of code which just initialises the
runtime with a test to make sure that it hasn't been done.  I was just
trying to find a more elegant solution.

In fact thinking about the following situation.  Mercury calls C# which
then calls a Mercury library to do some of its functionality.  In this
case the library should definitely not re-initialise the runtime.

> > The simple solution would be to hard code the call to io__init_state
> > from the .cctor of io instead of from MR_init_runtime.
> When you say hard-coding the call, do you mean having the
> compiler automatically insert that call when it is generating
> the .cctor for io, like the way it currently inserts the call to
> private_builtin.init_runtime/0 in main/2?
> You'd need to be careful about the order of initialization.
> In particular, io__init_state/2 requires that the type_ctor_infos
> for other modules, e.g. string and int, be initialized first.
> Initialization of the Mercury library requires several phases.
> 	1. Allocate all the type_ctor_info structures,
> 	   but do not fill them in yet.
> 	   In this phase, each module's initialization code
> 	   must not yet refer to variables in any other module.
> 	2. Fill in all the type_ctor_info structures
> 	   This phase may refer to the addresses of type_ctor_info
> 	   structures in other modules, but not to their contents.
> 	3. io__init_state/2
> 	   This phase may need to refer to the contents of
> 	   type_ctor_info structures in other modules,
> 	   e.g. to determine the address of the comparison
> 	   function for "int" in order to insert the stream_ids
> 	   into the stream_info database.
> Currently phases 1 and 2 are done in the .cctors, and phase 3 is done
> in main/2.
To be precise, phase 3 is done as the last thing in the .cctor of the
module containing main.

> Changing this so that phase 3 is done in the .cctors is not trivial,
> I think.  Suppose a user module m1 calls a library module, say term_io.m,
> which requires io__init_state to have been called.  Then with the
> current scheme for initialization, modified with your suggestion above,
> we'd get the following sequence:
> 	m1::entrypoint
> 	  term_io::.cctor
> 	    phase 1 for term_io
> 	    io::.cctor
> 	      phase 1 for io
> 	      term_io::.cctor
> 		skipped since already in progress
> 	      phase 2 for io
> 	      phase 3 for io
> 	    phase 2 for term_io
> As you can see, phase 3 for io occurs before phase 2 for term_io,
> which is a no-no.
> So if you want to do all three phases in the .cctors, then I think the
> code for the first two phases would have to be split out into separate
> functions, rather than being inline in the .cctors as it is currently.
Yes I see the problem here.

I don't think you can split the code out because remember the .cctor
gets run before the first use of any code of a class, so if you split it
out then it couldn't be run before the .cctor.  Instead you would to
have to introduce more state into the .cctor about globally which phases
had been completed.  Aaarrrggghhh it is making my head hurt just to
think about it.

> > However I believe we should be able to offer a better solution to this
> > problem, as it has come up before in the need to run a predicate once to
> > initialise a solver engine for HAL and I have encountered a similar
> > problem where one needs to run some setup routines before running any of
> > the MS sockets stuff.
> > 
> > Does anyone have any suggestions?
> Any automatic and fully general solution would need to be quite
> complicated in order to deal with complex initialization dependencies
> such as the three phase initialization mentioned above.  I suspect that
> such solutions are unlikely to be of sufficient benefit to be worth the
> costs at this time.
I agree it would be quite messy to implement this as evidenced by the
sore head from above.

> So for now I suggest that you document this issue in the Mercury user's
> guide and/or the language reference manual, and for cases where Mercury
> on .NET is used for implementing a library, leave it up to library users
> to explicitly invoke the initialization.
As pointed out above ML_runtime_init will need to be modified to ensure
that it isn't run twice.  I think it also needs to be made thread safe,
for the cases where the Mercury library is used by say a COM+ component.

> P.S. Pete, please fix the regressions that your earlier changes in April
> introduced before starting development of any new features!
A checked out debug version is all built and ready to go, but
unfortunately because of other pressing deadlines here I am unable to
fix it.  At the moment I am just fixing things on my critical path for a
demo that we need to do.  SORRY!
mercury-developers mailing list
Post messages to:       mercury-developers at cs.mu.oz.au
Administrative Queries: owner-mercury-developers at cs.mu.oz.au
Subscriptions:          mercury-developers-request at cs.mu.oz.au

More information about the developers mailing list