Hi!
I've trouble to create a named pluggable primitive DLL. I tried to figure out what to do from the sources (mostly Andrew's pdf document and the FlippyArray2 code) but nothing works. I don't know what goes wrong. I'm using Windows 98.
Here's my code. I use VC++ 5 to compile a DLL (non-MFC DLL project) which seems to work.
----------TestPlugin.c---------- #include "sqVirtualMachine.h"
#define EXPORT __declspec(dllexport)
static struct VirtualMachine *VM;
EXPORT char *getModuleName(void) { return "TestPlugin 24 Juni 2000"; }
EXPORT int setInterpreter(struct VirtualMachine *interpreterProxy) { VM = interpreterProxy; return 1; }
EXPORT void test(void) { int test = VM->stackIntegerValue(0); VM->popthenPush(1, VM->integerObjectOf(-test)); } ----------End TestPlugin.c----------
I use Squeak 2.8 (the version Andreas provided, I've no VM source and didn'T rebuilt that VM) and the following method to test my primitive
test: anInteger "TestPlugin new test: 42"
<primitive: 'test' module: 'TestPlugin'> ^ nil
and always get "nil", not "-42" as answer. When I use the FFI to call each function, it works. The first function answers the string, the second 1 and I can also call "test". I added a
MessageBox(NULL, "test", "TestPlugin", MB_OK);
to get some feedback. It seems that while getModuleName is correctly called, it doesn't find the other functions.
What goes wrong? Where can I find better/more recent documentation? Please help!
PS: Is there a method to unload a DLL? Now I've always have to restart Squeak if I what to replace my DLL.
bye -- Stefan Matthias Aust // Bevor wir fallen, fallen wir lieber auf
Stefan Matthias Aust wrote:
static struct VirtualMachine *VM;
EXPORT int setInterpreter(struct VirtualMachine *interpreterProxy) { VM = interpreterProxy; return 1; }
EXPORT void test(void) { int test = VM->stackIntegerValue(0); VM->popthenPush(1, VM->integerObjectOf(-test)); }
I haven't looked into the latest pluggable primitive stuff, but the approach implied in this code concerns me for use in a Windows DLL. When exactly does "setInterpreter" get called? By whom? How often if you are running multiple Squeak EXEs?
A Windows DLL shares its globals with all users of it. Assume "setInterpreter" is called once by each EXE using the DLL. This would mean that if you started up two Squeak sessions, the second one would also set its interpreter, setting the shared global variable. This means calls by the first VM to "test" afterward would now internally use the reference to the second VM, which because of address space issues may point to garbage instead of either VM.
It is my understanding that the "right" way to do this under Windows to allow multiple sessions to use the same DLL is to pass a pointer to the VM into every call.
Remember: the stack used by DLL functions is provided by the caller; the global variable space is shared by all users of the DLL (and is unique to the DLL). Also, remember that since different Windows processes may have different address spaces, a valid pointer in one address space may point to garbage in another address space unless the pointer is explicitly created as shared memory. I believe the address space is always resolved relative to the caller.
Note that the code above is completely valid and appropriate if it is statically linked into the VM. In that case, it would share the VM's global space, and a new global storage location for the modules VM pointer would of course then be allocated for every new application process running the VM
Note also that the above code might also be valid in a DLL if the VM was a shared resource used by all callers of the DLL. This would be valid in the case where all Squeak EXEs shared one running shared VM, and multi-threading issues were resolved using locks and semaphores (perhaps with the DLL function getting a lock on the VM before proceeding), VM calls were rentrant, and any namespace issues were resolved. I believe this is now the case with Python (whose VM is in a DLL under Windows) -- but as I understand it was tough sledding to do it and it requires care in its use. This takes us back to some post years ago on this list related to the difficulty of adding reentrancy in Squeak...
-Paul Fernhout Kurtz-Fernhout Software ========================================================= Developers of custom software and educational simulations Creators of the Garden with Insight(TM) garden simulator http://www.kurtz-fernhout.com
At 08:13 24.06.00 -0400, Paul Fernhout wrote:
I haven't looked into the latest pluggable primitive stuff, but the approach implied in this code concerns me for use in a Windows DLL. When exactly does "setInterpreter" get called? By whom?
Once per Squeak after the DLL was successfully loaded, getModuleName() was called and right before the first pluggable primitive is executed.
How often if you are running multiple Squeak EXEs?
Probably once per Squeak - I haven't checked. But that's not the problem - I confirmed that it was never called - because of the name I returned in getModuleName().
A Windows DLL shares its globals with all users of it.
Really? I hoped that each instance would get its own set of globals...
[...] This would mean that if you started up two Squeak sessions, the second one would also set its interpreter, setting the shared global variable.
That would be bad. But this would actually also break all existing Plugins then as all other also store their VM proxy pointer in a static global variable.
-Paul Fernhout
Thank you for your help.
bye -- Stefan Matthias Aust // Bevor wir fallen, fallen wir lieber auf
All-
I need to retract these comments regarding possible shared global variable difficulties with Squeak pluggable primitives as DLLs under Win32.
It turns out that what I said regarding problems with shared globals between applications (processes) is only the default for Win16 (so my knowledge on this was out of date).
Under Win32, by default, every application (process) has its own copy of global variables in a DLL. So there is no problem I can see now to this approach for Win32 pluggable primitives.
See for example: http://x71.deja.com/getdoc.xp?AN=603379772&search=thread&CONTEXT=965...
Someone on the MzScheme mailing list http://www.cs.rice.edu/CS/PLT/packages/mzscheme/index.html was kind enough to point out my misunderstanding regarding Win32 DLLs and the default case for globals, and I didn't want to let my passing on this misinformation cause any more confusion here.
-Paul Fernhout Kurtz-Fernhout Software ========================================================= Developers of custom software and educational simulations Creators of the Garden with Insight(TM) garden simulator http://www.kurtz-fernhout.com
Paul Fernhout wrote:
Stefan Matthias Aust wrote:
static struct VirtualMachine *VM;
EXPORT int setInterpreter(struct VirtualMachine *interpreterProxy) { VM = interpreterProxy; return 1; }
EXPORT void test(void) { int test = VM->stackIntegerValue(0); VM->popthenPush(1, VM->integerObjectOf(-test)); }
I haven't looked into the latest pluggable primitive stuff, but the approach implied in this code concerns me for use in a Windows DLL. When exactly does "setInterpreter" get called? By whom? How often if you are running multiple Squeak EXEs?
A Windows DLL shares its globals with all users of it. Assume "setInterpreter" is called once by each EXE using the DLL. This would mean that if you started up two Squeak sessions, the second one would also set its interpreter, setting the shared global variable. This means calls by the first VM to "test" afterward would now internally use the reference to the second VM, which because of address space issues may point to garbage instead of either VM.
It is my understanding that the "right" way to do this under Windows to allow multiple sessions to use the same DLL is to pass a pointer to the VM into every call.
Remember: the stack used by DLL functions is provided by the caller; the global variable space is shared by all users of the DLL (and is unique to the DLL). Also, remember that since different Windows processes may have different address spaces, a valid pointer in one address space may point to garbage in another address space unless the pointer is explicitly created as shared memory. I believe the address space is always resolved relative to the caller.
Note that the code above is completely valid and appropriate if it is statically linked into the VM. In that case, it would share the VM's global space, and a new global storage location for the modules VM pointer would of course then be allocated for every new application process running the VM
Note also that the above code might also be valid in a DLL if the VM was a shared resource used by all callers of the DLL. This would be valid in the case where all Squeak EXEs shared one running shared VM, and multi-threading issues were resolved using locks and semaphores (perhaps with the DLL function getting a lock on the VM before proceeding), VM calls were rentrant, and any namespace issues were resolved. I believe this is now the case with Python (whose VM is in a DLL under Windows) -- but as I understand it was tough sledding to do it and it requires care in its use. This takes us back to some post years ago on this list related to the difficulty of adding reentrancy in Squeak...
-Paul Fernhout Kurtz-Fernhout Software ========================================================= Developers of custom software and educational simulations Creators of the Garden with Insight(TM) garden simulator http://www.kurtz-fernhout.com
Hi Stefan
I would set the module name to "TestPlugin", not "TestPlugin 24 ...". It works for me on linux after this change.
Arjen
On Sat, 24 Jun 2000, Stefan Matthias Aust wrote:
Hi!
I've trouble to create a named pluggable primitive DLL. I tried to figure out what to do from the sources (mostly Andrew's pdf document and the FlippyArray2 code) but nothing works. I don't know what goes wrong. I'm using Windows 98.
Here's my code. I use VC++ 5 to compile a DLL (non-MFC DLL project) which seems to work.
----------TestPlugin.c---------- #include "sqVirtualMachine.h"
#define EXPORT __declspec(dllexport)
static struct VirtualMachine *VM;
EXPORT char *getModuleName(void) { return "TestPlugin 24 Juni 2000"; }
EXPORT int setInterpreter(struct VirtualMachine *interpreterProxy) { VM = interpreterProxy; return 1; }
EXPORT void test(void) { int test = VM->stackIntegerValue(0); VM->popthenPush(1, VM->integerObjectOf(-test)); } ----------End TestPlugin.c----------
I use Squeak 2.8 (the version Andreas provided, I've no VM source and didn'T rebuilt that VM) and the following method to test my primitive
test: anInteger "TestPlugin new test: 42"
<primitive: 'test' module: 'TestPlugin'> ^ nil
and always get "nil", not "-42" as answer. When I use the FFI to call each function, it works. The first function answers the string, the second 1 and I can also call "test". I added a
MessageBox(NULL, "test", "TestPlugin", MB_OK);
to get some feedback. It seems that while getModuleName is correctly called, it doesn't find the other functions.
What goes wrong? Where can I find better/more recent documentation? Please help!
PS: Is there a method to unload a DLL? Now I've always have to restart Squeak if I what to replace my DLL.
bye
Stefan Matthias Aust // Bevor wir fallen, fallen wir lieber auf
squeak-dev@lists.squeakfoundation.org