caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
From: tim@fungible.com (Tim Freeman)
To: jehenrik@yahoo.com
Cc: caml-list@inria.fr
Subject: [Caml-list] Type-safe DLL's with OO (was DLL-hell of O'Caml)
Date: Thu, 21 Mar 2002 07:13:58 -0700	[thread overview]
Message-ID: <1191-Thu21Mar2002093746-0800-tim@fungible.com> (raw)
In-Reply-To: <003001c1d0e2$9129b320$0b01a8c0@mit.edu> (jehenrik@yahoo.com)

From: "Jeff Henrikson" <jehenrik@yahoo.com>
>Imagine if the computer on your desk was built out of a (C
>based) source distro.  Not just a source distro, but one
>with the "all downstream dependencies recompile" policy.  (I
>don't think anybody has been so deluded to suggest this in a
>large system.)  Now suppose you write device drivers.
>Device driver programmers were frustrated enough before the
>linux kernel was modularized so that they could load and
>unload them without relinking the kernel and rebooting.  You
>mean to tell them now that they must recompile not only the
>kernel, but therefore the entire machine every time they
>make a one line bugfix?

No matter how this binary compatibility issue is resolved, you could
represent the API for the for the device driver as a virtual OCaml
object and have a function in the OS that registers a new instance of
a subclass of the virtual class as a device driver in future calls.

With this scheme, when you change a device driver, you wouldn't have
to recompile the world.  You'd have to recompile the code that
registers the device driver, and everything from there to the top
level of the program.  If the first thing the top level does is
register the new device driver, then that's just recompiling two
things, the device driver and the top level.

Unfortunately you'd then have to restart the "program" to get your new
device driver, and in this scenario the "program" is the operating
system.  Yuck.

If we assume good object oriented design at the API for the device
driver, the minimal support you'd need to do this without restarting
the operating system is to be able to dynamically load a module and do
the static initialization for it.  You don't need to access anything
inside the module.  You don't need to get any information out of your
DLL except by side effects of the static initialization.  In this case
the static initialization would do nothing but register new the device
driver.

I have sample code for this below.  It deals with upgraded API's in a
type-safe way.  It substitutes an "open" statement for loading and
initializing a DLL, since ocaml doesn't presently support loading
ocaml DLL's.  

This would also solve the issue of the lablgtk executable size that I
raised earlier.  The machinery is somewhat of a burden on the library
writer unless a code generator could be used.

I think this idea is mostly from Lakos' book "Large-Scale C++ Software
Design".

There would then be some value in teaching the garbage collector to
garbage collect object files from no-longer-used DLL's.  Otherwise if
you load enough device drivers you run out of VM.

-- 
Tim Freeman       
tim@fungible.com

P. S. Here's the illustration code.  Version 1 of the library has a
function "boring".  Version 2 of the library adds a function
"interesting" and a bugfix for "boring".  There are three
applications: v1_user that uses version 1, v2_user that uses version
2, and v1_user_against_v2 that simulates the version 1 application
linked against the version 2 library.  It sees the bugfix.

----- lib_static_v1_decl.ml -----
class virtual simple = object
  method virtual boring: unit
end;;
----- lib_static_v1.ml -----
let v1_ptr:Lib_static_v1_decl.simple option ref = ref None;;
let boring () =
  match !v1_ptr with
    Some f -> f#boring
  | None -> assert (false);;
----- lib_dynamic_v1.ml -----
class simpleImpl = object
  inherit Lib_static_v1_decl.simple
  method boring = print_string "Inside boring!\n"
end;;
Lib_static_v1.v1_ptr := Some new simpleImpl;;
----- v1_user.ml -----
(* I'd like to dynamically load Lib_dynamic_v1 here, but that*)
(* primitive doesn't exist, so I'll just open it instead.  Note that I*)
(* never directly use anything from it. *)
open Lib_dynamic_v1;;
Lib_static_v1.boring ();;
----- lib_static_v2_decl.ml -----
class virtual fancy = object
  inherit Lib_static_v1_decl.simple
  method virtual interesting: unit
end;;
----- lib_static_v2.ml -----
let v2_ptr:Lib_static_v2_decl.fancy option ref = ref None;;
include Lib_static_v1;;
let interesting () =
  match !v2_ptr with
    Some f -> f#interesting
  | None -> assert (false);;
----- lib_dynamic_v2.ml -----
class fancyImpl = object
  inherit Lib_static_v2_decl.fancy
  method boring = print_string "Inside bugfixed boring!\n"
  method interesting = print_string "Inside interesting!\n"
end;;
Lib_static_v1.v1_ptr := Some (new fancyImpl :> Lib_static_v1_decl.simple);;
Lib_static_v2.v2_ptr := Some new fancyImpl;;
----- v2_user.ml -----
(* I'd like to dynamically load Lib_dynamic_v2 here, but that*)
(* primitive doesn't exist, so I'll just open it instead.  Note that I*)
(* never directly use anything from it. *)
open Lib_dynamic_v2;;
Lib_static_v2.boring ();;
Lib_static_v2.interesting ();;
----- v1_user_against_v2.ml -----
(* The next line should really be loading a DLL.  We should pick up a*)
(* different file because the old DLL was updated in place, so if we*)
(* could really load ocaml dll's, this file would be identical to*)
(* v1_user.ml. *)
open Lib_dynamic_v2;;
Lib_static_v1.boring ();;
----- Makefile -----
.DELETE_ON_ERROR:
EXES = v2_user v1_user v1_user_against_v2

.PHONY: run
run: $(EXES)
	./v1_user
	./v2_user
	./v1_user_against_v2

.PHONY: clean
clean:
	rm -f $(EXES) *.cmo *.cmi *~ make.dep

make.dep: $(wildcard *.ml)
	ocamldep *.ml > make.dep
include make.dep

%.cmi %.cmo: %.ml
	ocamlc -c $+

v1_user: lib_static_v1.cmo lib_dynamic_v1.cmo v1_user.cmo
	ocamlc -o v1_user lib_static_v1_decl.cmo lib_static_v1.cmo lib_dynamic_v1.cmo v1_user.cmo
v2_user: lib_static_v2.cmo lib_dynamic_v2.cmo lib_static_v1.cmo lib_dynamic_v1.cmo v2_user.cmo
	ocamlc -o v2_user lib_static_v1_decl.cmo lib_static_v2_decl.cmo lib_static_v1.cmo lib_dynamic_v1.cmo lib_static_v2.cmo lib_dynamic_v2.cmo v2_user.cmo
v1_user_against_v2: lib_static_v2.cmo lib_dynamic_v2.cmo lib_static_v1.cmo lib_dynamic_v1.cmo v1_user_against_v2.cmo
	ocamlc -o v1_user_against_v2 lib_static_v1_decl.cmo lib_static_v2_decl.cmo lib_static_v1.cmo lib_dynamic_v1.cmo lib_static_v2.cmo lib_dynamic_v2.cmo v1_user_against_v2.cmo

-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners


  reply	other threads:[~2002-03-21 17:37 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2002-03-11  4:28 [Caml-list] The DLL-hell of O'Caml Mark D. Anderson
2002-03-11  7:12 ` Mattias Waldau
2002-03-11 12:15 ` Gerd Stolpmann
2002-03-12  0:19   ` Jeff Henrikson
2002-03-12 22:00     ` Gerd Stolpmann
2002-03-20 11:20       ` Fergus Henderson
2002-03-20 11:43         ` Jacques Garrigue
2002-03-20 17:16           ` Fergus Henderson
2002-03-20 12:53         ` Gerd Stolpmann
2002-03-20 13:05           ` Johan Georg Granström
2002-03-20 13:40             ` Gerd Stolpmann
2002-03-20 19:46               ` Alain Frisch
2002-03-20 20:39               ` Xavier Leroy
2002-03-20 21:16                 ` Markus Mottl
2002-03-21  9:07                 ` Warp
2002-03-21 10:18                 ` Christopher Quinn
2002-03-21 18:13                   ` Xavier Leroy
2002-03-21 14:13                 ` Jeff Henrikson
2002-03-21 14:13                   ` Tim Freeman [this message]
2002-03-21 18:10                   ` Xavier Leroy
2002-03-21 18:39                     ` Sven
2002-03-21 19:22                     ` james woodyatt
2002-03-21 19:43                     ` Jeff Henrikson
2002-03-22  2:02                     ` Brian Rogoff
2002-03-22 10:11                     ` Warp
2002-03-21 18:50                 ` Sven

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1191-Thu21Mar2002093746-0800-tim@fungible.com \
    --to=tim@fungible.com \
    --cc=caml-list@inria.fr \
    --cc=jehenrik@yahoo.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).