534 lines
11 KiB
C++
534 lines
11 KiB
C++
// mzobj.cxx : Implementation of CMzObj
|
|
|
|
#ifdef MZCOM_3M
|
|
/* xform.ss converts this file to mzobj3m.cxx: */
|
|
# define i64 /* ??? why does expansion produce i64? */
|
|
# include "mzobj3m.cxx"
|
|
#else
|
|
|
|
#include "scheme.h"
|
|
|
|
#ifdef MZ_PRECISE_GC
|
|
START_XFORM_SKIP;
|
|
#endif
|
|
|
|
#include "stdafx.h"
|
|
#include "resource.h"
|
|
|
|
#include "mzcom.h"
|
|
|
|
#ifdef MZ_PRECISE_GC
|
|
END_XFORM_SKIP;
|
|
#endif
|
|
|
|
#include "mzobj.h"
|
|
|
|
#ifndef MZ_PRECISE_GC
|
|
# define GC_CAN_IGNORE /* empty */
|
|
#endif
|
|
|
|
#ifdef MZ_PRECISE_GC
|
|
START_XFORM_SKIP;
|
|
#endif
|
|
|
|
static void ErrorBox(char *s) {
|
|
::MessageBox(NULL,s,"MzCOM",MB_OK);
|
|
}
|
|
|
|
#ifdef MZ_PRECISE_GC
|
|
END_XFORM_SKIP;
|
|
#endif
|
|
|
|
static THREAD_GLOBALS tg;
|
|
|
|
static Scheme_Env *env;
|
|
|
|
static BOOL *pErrorState;
|
|
static OLECHAR *wideError;
|
|
|
|
static HANDLE evalLoopSems[2];
|
|
static HANDLE exitSem;
|
|
|
|
static Scheme_Object *exn_catching_apply;
|
|
static Scheme_Object *exn_p;
|
|
static Scheme_Object *exn_message;
|
|
|
|
static Scheme_At_Exit_Callback_Proc at_exit_callback;
|
|
|
|
/* This indirection lets us delayload libmzsch.dll: */
|
|
#define scheme_false (scheme_make_false())
|
|
|
|
static Scheme_Object *_apply_thunk_catch_exceptions(Scheme_Object *f,
|
|
Scheme_Object **exn) {
|
|
Scheme_Object *v;
|
|
|
|
v = _scheme_apply(exn_catching_apply,1,&f);
|
|
|
|
/* v is a pair: (cons #t value) or (cons #f exn) */
|
|
|
|
if (SCHEME_TRUEP(SCHEME_CAR(v))) {
|
|
return SCHEME_CDR(v);
|
|
}
|
|
else {
|
|
*exn = SCHEME_CDR(v);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static Scheme_Object *extract_exn_message(Scheme_Object *v) {
|
|
if (SCHEME_TRUEP(_scheme_apply(exn_p,1,&v)))
|
|
return _scheme_apply(exn_message,1,&v);
|
|
else
|
|
return NULL; /* Not an exn structure */
|
|
}
|
|
|
|
static Scheme_Object *do_eval(void *s,int,Scheme_Object **) {
|
|
return scheme_eval_string_all((char *)s,env,TRUE);
|
|
}
|
|
|
|
static Scheme_Object *eval_string_or_get_exn_message(char *s) {
|
|
Scheme_Object *v;
|
|
Scheme_Object *exn;
|
|
|
|
v = _apply_thunk_catch_exceptions(scheme_make_closed_prim(do_eval,s),&exn);
|
|
/* value */
|
|
if (v) {
|
|
*pErrorState = FALSE;
|
|
return v;
|
|
}
|
|
|
|
v = extract_exn_message(exn);
|
|
/* exn */
|
|
if (v) {
|
|
*pErrorState = TRUE;
|
|
return v;
|
|
}
|
|
|
|
/* `raise' was called on some arbitrary value */
|
|
return exn;
|
|
}
|
|
|
|
OLECHAR *wideStringFromSchemeObj(Scheme_Object *obj,char *fmt,int fmtlen) {
|
|
char *s;
|
|
OLECHAR *wideString;
|
|
int len;
|
|
|
|
s = scheme_format_utf8(fmt,fmtlen,1,&obj,NULL);
|
|
len = strlen(s);
|
|
wideString = (OLECHAR *)scheme_malloc((len + 1) * sizeof(OLECHAR));
|
|
MultiByteToWideChar(CP_ACP,(DWORD)0,s,len,wideString,len + 1);
|
|
wideString[len] = 0;
|
|
return wideString;
|
|
}
|
|
|
|
void exitHandler(int) {
|
|
if (at_exit_callback) at_exit_callback();
|
|
ReleaseSemaphore(exitSem,1,NULL);
|
|
_endthreadex(0);
|
|
}
|
|
|
|
void setupSchemeEnv(Scheme_Env *in_env)
|
|
{
|
|
char *wrapper;
|
|
char exeBuff[260];
|
|
HMODULE mod;
|
|
static BOOL registered;
|
|
|
|
if (!registered) {
|
|
scheme_register_static(&env,sizeof(env));
|
|
scheme_register_static(&exn_catching_apply,sizeof(exn_catching_apply));
|
|
scheme_register_static(&exn_p,sizeof(exn_p));
|
|
scheme_register_static(&exn_message,sizeof(exn_message));
|
|
registered = TRUE;
|
|
}
|
|
|
|
env = in_env;
|
|
|
|
if (env == NULL) {
|
|
ErrorBox("Can't create Scheme environment");
|
|
_endthreadex(0);
|
|
}
|
|
|
|
// set up collection paths, based on Racket startup
|
|
|
|
mod = GetModuleHandle("mzcom.exe");
|
|
GetModuleFileName(mod,exeBuff,sizeof(exeBuff));
|
|
|
|
scheme_add_global("mzcom-exe",scheme_make_utf8_string(exeBuff),env);
|
|
scheme_set_exec_cmd(exeBuff);
|
|
scheme_set_collects_path(scheme_make_path("collects"));
|
|
scheme_init_collection_paths(env, scheme_make_null());
|
|
|
|
// initialize namespace
|
|
|
|
scheme_namespace_require(scheme_intern_symbol("scheme"));
|
|
|
|
// set up exception trapping
|
|
|
|
wrapper =
|
|
"(lambda (thunk) "
|
|
"(with-handlers ([void (lambda (exn) (cons #f exn))]) "
|
|
"(cons #t (thunk))))";
|
|
|
|
exn_catching_apply = scheme_eval_string(wrapper,env);
|
|
exn_p = scheme_builtin_value("exn?");
|
|
exn_message = scheme_builtin_value("exn-message");
|
|
}
|
|
|
|
static int do_evalLoop(Scheme_Env *env, int argc, char **_args)
|
|
{
|
|
LPVOID args = (LPVOID)_args;
|
|
HRESULT *pHr;
|
|
BOOL doEval;
|
|
UINT len;
|
|
DWORD waitVal;
|
|
char *narrowInput;
|
|
Scheme_Object *outputObj;
|
|
Scheme_Object *sleepFun;
|
|
OLECHAR *outputBuffer;
|
|
THREAD_GLOBALS *pTg;
|
|
HANDLE readSem;
|
|
HANDLE writeSem;
|
|
HANDLE resetSem;
|
|
HANDLE resetDoneSem;
|
|
BSTR **ppInput;
|
|
BSTR *pOutput, po;
|
|
MSG msg;
|
|
|
|
// make sure all Racket calls are in this thread
|
|
|
|
setupSchemeEnv(env);
|
|
|
|
scheme_set_exit(exitHandler);
|
|
sleepFun = scheme_builtin_value("sleep");
|
|
|
|
pTg = (THREAD_GLOBALS *)args;
|
|
|
|
ppInput = pTg->ppInput;
|
|
pOutput = pTg->pOutput;
|
|
pHr = pTg->pHr;
|
|
readSem = pTg->readSem;
|
|
writeSem = pTg->writeSem;
|
|
resetSem = pTg->resetSem;
|
|
resetDoneSem = pTg->resetDoneSem;
|
|
pErrorState = pTg->pErrorState;
|
|
|
|
while (1) {
|
|
|
|
doEval = FALSE;
|
|
|
|
while (doEval == FALSE) {
|
|
waitVal = MsgWaitForMultipleObjects(2,evalLoopSems,FALSE,
|
|
5,QS_ALLINPUT);
|
|
|
|
switch (waitVal) {
|
|
|
|
case WAIT_TIMEOUT :
|
|
|
|
scheme_apply(sleepFun,0,NULL);
|
|
break;
|
|
|
|
case WAIT_OBJECT_0 + 1:
|
|
|
|
// reset semaphore signalled
|
|
|
|
setupSchemeEnv(scheme_basic_env());
|
|
ReleaseSemaphore(resetDoneSem,1,NULL);
|
|
|
|
break;
|
|
|
|
case WAIT_OBJECT_0 + 2:
|
|
|
|
// Windows msg
|
|
|
|
while (PeekMessage(&msg,NULL,0x400,0x400,PM_REMOVE)) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
scheme_apply(sleepFun,0,NULL);
|
|
|
|
break;
|
|
|
|
default :
|
|
|
|
// got string to eval
|
|
|
|
doEval = TRUE;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
len = SysStringLen(**ppInput);
|
|
|
|
narrowInput = (char *)scheme_malloc(len + 1);
|
|
|
|
scheme_dont_gc_ptr(narrowInput);
|
|
|
|
WideCharToMultiByte(CP_ACP,(DWORD)0,
|
|
**ppInput,len,
|
|
narrowInput,len + 1,
|
|
NULL,NULL);
|
|
|
|
narrowInput[len] = '\0';
|
|
|
|
outputObj = eval_string_or_get_exn_message(narrowInput);
|
|
|
|
scheme_gc_ptr_ok(narrowInput);
|
|
|
|
if (*pErrorState) {
|
|
wideError = wideStringFromSchemeObj(outputObj,"Racket error: ~a",18);
|
|
po = SysAllocString(L"");
|
|
*pOutput = po;
|
|
*pHr = E_FAIL;
|
|
}
|
|
else {
|
|
outputBuffer = wideStringFromSchemeObj(outputObj,"~s",2);
|
|
po = SysAllocString(outputBuffer);
|
|
*pOutput = po;
|
|
*pHr = S_OK;
|
|
}
|
|
|
|
ReleaseSemaphore(writeSem,1,NULL);
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void record_at_exit(Scheme_At_Exit_Callback_Proc p) XFORM_SKIP_PROC
|
|
{
|
|
at_exit_callback = p;
|
|
}
|
|
|
|
static __declspec(thread) void *tls_space;
|
|
|
|
static unsigned WINAPI evalLoop(void *args) XFORM_SKIP_PROC {
|
|
scheme_register_tls_space(&tls_space, 0);
|
|
scheme_set_atexit(record_at_exit);
|
|
return scheme_main_setup(1, do_evalLoop, 0, (char **)args);
|
|
}
|
|
|
|
#ifdef MZ_PRECISE_GC
|
|
START_XFORM_SKIP;
|
|
#endif
|
|
|
|
void CMzObj::startMzThread(void) {
|
|
tg.pHr = &hr;
|
|
tg.ppInput = &globInput;
|
|
tg.pOutput = &globOutput;
|
|
tg.readSem = readSem;
|
|
tg.writeSem = writeSem;
|
|
tg.resetSem = resetSem;
|
|
tg.resetDoneSem = resetDoneSem;
|
|
tg.pErrorState = &errorState;
|
|
|
|
threadHandle = (HANDLE)_beginthreadex(NULL, 0, evalLoop, &tg, 0, NULL);
|
|
}
|
|
|
|
|
|
CMzObj::CMzObj(void) {
|
|
inputMutex = NULL;
|
|
readSem = NULL;
|
|
threadHandle = NULL;
|
|
|
|
inputMutex = CreateSemaphore(NULL,1,1,NULL);
|
|
if (inputMutex == NULL) {
|
|
ErrorBox("Can't create input mutex");
|
|
return;
|
|
}
|
|
|
|
readSem = CreateSemaphore(NULL,0,1,NULL);
|
|
|
|
if (readSem == NULL) {
|
|
ErrorBox("Can't create read semaphore");
|
|
return;
|
|
}
|
|
|
|
writeSem = CreateSemaphore(NULL,0,1,NULL);
|
|
|
|
if (writeSem == NULL) {
|
|
ErrorBox("Can't create write semaphore");
|
|
return;
|
|
}
|
|
|
|
exitSem = CreateSemaphore(NULL,0,1,NULL);
|
|
|
|
if (exitSem == NULL) {
|
|
ErrorBox("Can't create exit semaphore");
|
|
return;
|
|
}
|
|
|
|
resetSem = CreateSemaphore(NULL,0,1,NULL);
|
|
|
|
if (resetSem == NULL) {
|
|
ErrorBox("Can't create reset semaphore");
|
|
return;
|
|
}
|
|
|
|
resetDoneSem = CreateSemaphore(NULL,0,1,NULL);
|
|
|
|
if (resetSem == NULL) {
|
|
ErrorBox("Can't create reset-done semaphore");
|
|
return;
|
|
}
|
|
|
|
evalLoopSems[0] = readSem;
|
|
evalLoopSems[1] = resetSem;
|
|
evalDoneSems[0] = writeSem;
|
|
evalDoneSems[1] = exitSem;
|
|
|
|
startMzThread();
|
|
|
|
}
|
|
|
|
void CMzObj::killMzThread(void) {
|
|
if (threadHandle) {
|
|
DWORD threadStatus;
|
|
|
|
GetExitCodeThread(threadHandle,&threadStatus);
|
|
|
|
if (threadStatus == STILL_ACTIVE) {
|
|
TerminateThread(threadHandle,0);
|
|
}
|
|
|
|
CloseHandle(threadHandle);
|
|
|
|
threadHandle = NULL;
|
|
}
|
|
}
|
|
|
|
CMzObj::~CMzObj(void) {
|
|
|
|
killMzThread();
|
|
|
|
if (readSem) {
|
|
CloseHandle(readSem);
|
|
}
|
|
|
|
if (writeSem) {
|
|
CloseHandle(writeSem);
|
|
}
|
|
|
|
if (exitSem) {
|
|
CloseHandle(exitSem);
|
|
}
|
|
|
|
if (inputMutex) {
|
|
CloseHandle(inputMutex);
|
|
}
|
|
}
|
|
|
|
void CMzObj::RaiseError(const OLECHAR *msg) {
|
|
BSTR bstr;
|
|
ICreateErrorInfo *pICreateErrorInfo;
|
|
IErrorInfo *pIErrorInfo;
|
|
|
|
bstr = SysAllocString(msg);
|
|
|
|
if (CreateErrorInfo(&pICreateErrorInfo) == S_OK &&
|
|
pICreateErrorInfo != NULL) {
|
|
pICreateErrorInfo->SetGUID(IID_IMzObj);
|
|
pICreateErrorInfo->SetDescription((LPOLESTR)msg);
|
|
pICreateErrorInfo->SetSource((LPOLESTR)L"MzCOM.MzObj");
|
|
if (pICreateErrorInfo->QueryInterface(IID_IErrorInfo,
|
|
(void **)&pIErrorInfo) == S_OK &&
|
|
pIErrorInfo != NULL) {
|
|
SetErrorInfo(0,pIErrorInfo);
|
|
}
|
|
}
|
|
|
|
Fire_SchemeError(bstr);
|
|
SysFreeString(bstr);
|
|
}
|
|
|
|
BOOL CMzObj::testThread(void) {
|
|
DWORD threadStatus;
|
|
|
|
if (threadHandle == NULL) {
|
|
RaiseError(L"No evaluator");
|
|
return FALSE;
|
|
}
|
|
|
|
if (GetExitCodeThread(threadHandle,&threadStatus) == 0) {
|
|
RaiseError(L"Evaluator may be terminated");
|
|
}
|
|
|
|
if (threadStatus != STILL_ACTIVE) {
|
|
RaiseError(L"Evaluator terminated");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CMzObj
|
|
|
|
STDMETHODIMP CMzObj::Eval(BSTR input, BSTR *output) {
|
|
if (!testThread()) {
|
|
return E_ABORT;
|
|
}
|
|
|
|
WaitForSingleObject(inputMutex,INFINITE);
|
|
globInput = &input;
|
|
// allow evaluator to read
|
|
ReleaseSemaphore(readSem,1,NULL);
|
|
|
|
// wait until evaluator done or eval thread terminated
|
|
if (WaitForMultipleObjects(2,evalDoneSems,FALSE,INFINITE) ==
|
|
WAIT_OBJECT_0 + 1) {
|
|
RaiseError(L"Scheme terminated evaluator");
|
|
return E_FAIL;
|
|
}
|
|
|
|
*output = globOutput;
|
|
ReleaseSemaphore(inputMutex,1,NULL);
|
|
|
|
if (errorState) {
|
|
RaiseError(wideError);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
BOOL WINAPI dlgProc(HWND hDlg,UINT msg,WPARAM wParam,LPARAM) {
|
|
switch(msg) {
|
|
case WM_INITDIALOG :
|
|
SetDlgItemText(hDlg,MZCOM_URL,
|
|
"http://www.cs.rice.edu/CS/PLT/packages/mzcom/");
|
|
return TRUE;
|
|
case WM_COMMAND :
|
|
switch (LOWORD(wParam)) {
|
|
case IDOK :
|
|
case IDCANCEL :
|
|
EndDialog(hDlg,0);
|
|
return FALSE;
|
|
}
|
|
default :
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP CMzObj::About() {
|
|
DialogBox(globHinst,MAKEINTRESOURCE(ABOUTBOX),NULL,dlgProc);
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CMzObj::Reset() {
|
|
if (!testThread()) {
|
|
return E_ABORT;
|
|
}
|
|
|
|
ReleaseSemaphore(resetSem,1,NULL);
|
|
WaitForSingleObject(resetDoneSem,INFINITE);
|
|
return S_OK;
|
|
}
|
|
|
|
#ifdef MZ_PRECISE_GC
|
|
END_XFORM_SKIP;
|
|
#endif
|
|
|
|
#endif // MZCOM_3M
|