* running script in the PHP/Zend engine must take place on the engine
* thread. Likewise, calling back to the host must take place on the base
* thread - the thread that set the script site.
- *
- * For talking to the site from engine thread, we use an invisible window:
- * the window processing is guaranteed to occur in the correct thread,
- * and the message queue provides a useful synchronization device.
- *
- * For talking to the engine from any other thread, the engine thread waits
- * for messages to arrive at it's message queue. Since the only API for
- * dealing with thread messages is asynchronous, we use a mutex to ensure
- * that only one thread can talk to the engine at a time, and an event
- * object to signal to it that the processing is complete.
- *
* */
#define _WIN32_DCOM
#include "ext/com/php_COM.h"
#include "ext/com/conversion.h"
}
+#include "php_ticks.h"
#include "php4as_scriptengine.h"
#include "php4as_classfactory.h"
#include <objbase.h>
-enum fragtype {
- FRAG_MAIN,
- FRAG_SCRIPTLET,
- FRAG_PROCEDURE
-};
-
-typedef struct {
- enum fragtype fragtype;
- zend_op_array *opcodes;
- char *code;
- int persistent; /* should be retained for Clone */
- int executed; /* for "main" */
- char *functionname;
- unsigned int codelen;
- unsigned int starting_line;
- TPHPScriptingEngine *engine;
- void *ptr;
-} code_frag;
-
-#define FRAG_CREATE_FUNC (char*)-1
-static code_frag *compile_code_fragment(
- enum fragtype fragtype,
- char *functionname,
- LPCOLESTR code,
- ULONG starting_line,
- EXCEPINFO *excepinfo,
- TPHPScriptingEngine *engine
- TSRMLS_DC);
-
-static int execute_code_fragment(code_frag *frag,
- VARIANT *varResult,
- EXCEPINFO *excepinfo
- TSRMLS_DC);
-static void free_code_fragment(code_frag *frag);
-static code_frag *clone_code_fragment(code_frag *frag, TPHPScriptingEngine *engine TSRMLS_DC);
-
-/* Magic for handling threading correctly */
-static inline HRESULT SEND_THREAD_MESSAGE(TPHPScriptingEngine *engine, LONG msg, WPARAM wparam, LPARAM lparam TSRMLS_DC)
+/* {{{ trace */
+static inline void trace(char *fmt, ...)
{
- if (engine->m_enginethread == 0)
- return E_UNEXPECTED;
- if (tsrm_thread_id() == (engine)->m_enginethread)
- return (engine)->engine_thread_handler((msg), (wparam), (lparam), NULL TSRMLS_CC);
- return (engine)->SendThreadMessage((msg), (wparam), (lparam));
-}
-
+ va_list ap;
+ char buf[4096];
+ sprintf(buf, "T=%08x ", tsrm_thread_id());
+ OutputDebugString(buf);
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ OutputDebugString(buf);
+
+ va_end(ap);
+}
+/* }}} */
/* {{{ scriptstate_to_string */
static const char *scriptstate_to_string(SCRIPTSTATE ss)
{
}
}
/* }}} */
-
-/* {{{ trace */
-static inline void trace(char *fmt, ...)
-{
- va_list ap;
- char buf[4096];
-
- sprintf(buf, "T=%08x ", tsrm_thread_id());
- OutputDebugString(buf);
-
- va_start(ap, fmt);
- vsnprintf(buf, sizeof(buf), fmt, ap);
-
- OutputDebugString(buf);
-
- va_end(ap);
-}
-/* }}} */
-
/* {{{ TWideString */
/* This class helps manipulate strings from OLE.
* It does not use emalloc, so it is better suited for passing pointers
};
/* }}} */
+/* {{{ code fragment structures */
+enum fragtype {
+ FRAG_MAIN,
+ FRAG_SCRIPTLET,
+ FRAG_PROCEDURE
+};
+
+typedef struct {
+ enum fragtype fragtype;
+ zend_op_array *opcodes;
+ char *code;
+ int persistent; /* should be retained for Clone */
+ int executed; /* for "main" */
+ char *functionname;
+ unsigned int codelen;
+ unsigned int starting_line;
+ TPHPScriptingEngine *engine;
+ void *ptr;
+} code_frag;
+
+#define FRAG_CREATE_FUNC (char*)-1
+static code_frag *compile_code_fragment(
+ enum fragtype fragtype,
+ char *functionname,
+ LPCOLESTR code,
+ ULONG starting_line,
+ EXCEPINFO *excepinfo,
+ TPHPScriptingEngine *engine
+ TSRMLS_DC);
+
+static int execute_code_fragment(code_frag *frag,
+ VARIANT *varResult,
+ EXCEPINFO *excepinfo
+ TSRMLS_DC);
+static void free_code_fragment(code_frag *frag);
+static code_frag *clone_code_fragment(code_frag *frag, TPHPScriptingEngine *engine TSRMLS_DC);
+
+/* }}} */
+
+/* Magic for handling threading correctly */
+static inline HRESULT SEND_THREAD_MESSAGE(TPHPScriptingEngine *engine, LONG msg, WPARAM wparam, LPARAM lparam TSRMLS_DC)
+{
+ if (engine->m_enginethread == 0)
+ return E_UNEXPECTED;
+ if (tsrm_thread_id() == (engine)->m_enginethread)
+ return (engine)->engine_thread_handler((msg), (wparam), (lparam), NULL TSRMLS_CC);
+ return (engine)->SendThreadMessage((msg), (wparam), (lparam));
+}
+
+/* These functions do some magic so that interfaces can be
+ * used across threads without worrying about marshalling
+ * or not marshalling, as appropriate.
+ * Win95 without DCOM 1.1, and NT SP 2 or lower do not have
+ * the GIT; so we emulate the GIT using other means.
+ * If you trace problems back to this code, installing the relevant
+ * SP should solve them.
+ * */
+static inline HRESULT GIT_get(DWORD cookie, REFIID riid, void **obj)
+{
+ IGlobalInterfaceTable *git;
+ HRESULT ret;
+
+ if (SUCCEEDED(CoCreateInstance(CLSID_StdGlobalInterfaceTable, NULL,
+ CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable,
+ (void**)&git))) {
+
+ ret = git->GetInterfaceFromGlobal(cookie, riid, obj);
+ git->Release();
+ return ret;
+ }
+ return CoGetInterfaceAndReleaseStream((LPSTREAM)cookie, riid, obj);
+}
+
+static inline HRESULT GIT_put(IUnknown *unk, REFIID riid, DWORD *cookie)
+{
+ IGlobalInterfaceTable *git;
+ HRESULT ret;
+
+ if (SUCCEEDED(CoCreateInstance(CLSID_StdGlobalInterfaceTable, NULL,
+ CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable,
+ (void**)&git))) {
+
+ ret = git->RegisterInterfaceInGlobal(unk, riid, cookie);
+ git->Release();
+ return ret;
+ }
+ return CoMarshalInterThreadInterfaceInStream(riid, unk, (LPSTREAM*)cookie);
+}
+
+static inline HRESULT GIT_revoke(DWORD cookie, IUnknown *unk)
+{
+ IGlobalInterfaceTable *git;
+ HRESULT ret;
+
+ if (SUCCEEDED(CoCreateInstance(CLSID_StdGlobalInterfaceTable, NULL,
+ CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable,
+ (void**)&git))) {
+
+ ret = git->RevokeInterfaceFromGlobal(cookie);
+ git->Release();
+ }
+ /* Kill remote clients */
+ return CoDisconnectObject(unk, 0);
+}
+
+
+
/* {{{ A generic stupid IDispatch implementation */
class IDispatchImpl:
public IDispatch
code_frag *m_frag;
DWORD m_procflags;
TPHPScriptingEngine *m_engine;
+ DWORD m_gitcookie;
STDMETHODIMP Invoke( DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
DISPPARAMS FAR* pDispParams, VARIANT FAR* pVarResult, EXCEPINFO FAR* pExcepInfo,
}
ScriptProcedureDispatch() {
m_refcount = 1;
+ GIT_put((IDispatch*)this, IID_IDispatch, &m_gitcookie);
}
};
/* }}} */
if (frag->ptr) {
ScriptProcedureDispatch *disp = (ScriptProcedureDispatch*)frag->ptr;
disp->Release();
- CoDisconnectObject((IUnknown*)disp, 0);
+ GIT_revoke(disp->m_gitcookie, (IDispatch*)disp);
frag->ptr = NULL;
}
break;
CloseHandle(m_engine_thread_handle);
}
+void activescript_run_ticks(int count)
+{
+ MSG msg;
+ TSRMLS_FETCH();
+ TPHPScriptingEngine *engine;
+
+ trace("ticking %d\n", count);
+
+ engine = (TPHPScriptingEngine*)SG(server_context);
+
+/* PostThreadMessage(engine->m_enginethread, PHPSE_DUMMY_TICK, 0, 0); */
+
+ while(PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
+ if (msg.hwnd) {
+ PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ } else {
+ break;
+ }
+ }
+}
+
/* Synchronize with the engine thread */
HRESULT TPHPScriptingEngine::SendThreadMessage(LONG msg, WPARAM wparam, LPARAM lparam)
{
MSG msg;
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
//trace("dispatching message while waiting\n");
+ TranslateMessage(&msg);
DispatchMessage(&msg);
}
} else if (result == WAIT_TIMEOUT) {
php_request_startup(TSRMLS_C);
PG(during_request_startup) = 0;
+ trace("\n\n *** ticks func at %08x %08x ***\n\n\n", activescript_run_ticks, &activescript_run_ticks);
+// php_add_tick_function(activescript_run_ticks);
}
break;
{
struct php_active_script_get_dispatch_info *info = (struct php_active_script_get_dispatch_info *)lParam;
IDispatch *disp = NULL;
- char *itemname;
- unsigned int itemlen;
if (info->pstrItemName != NULL) {
zval **tmp;
-
- itemname = php_OLECHAR_to_char((OLECHAR*)info->pstrItemName, &itemlen, CP_ACP TSRMLS_CC);
-
+ /* use this rather than php_OLECHAR_to_char because we want to avoid emalloc here */
+ TWideString itemname(info->pstrItemName);
+
/* Get that item from the global namespace.
* If it is an object, export it as a dispatchable object.
* */
- if (zend_hash_find(&EG(symbol_table), itemname, itemlen+1, (void**)&tmp) == SUCCESS) {
+ if (zend_hash_find(&EG(symbol_table), itemname.ansi_string(),
+ itemname.ansi_len() + 1, (void**)&tmp) == SUCCESS) {
if (Z_TYPE_PP(tmp) == IS_OBJECT) {
+ /* FIXME: if this causes an allocation (emalloc) and we are
+ * not in the engine thread, things could get ugly!!! */
disp = php_COM_export_object(*tmp TSRMLS_CC);
}
}
- trace("%08x: GetScriptDispatch(%s --> %08x)\n", this, itemname, disp);
-
- efree(itemname);
} else {
-#if 0
- zval *obj;
-
- MAKE_STD_ZVAL(obj);
- object_init(obj);
- disp = php_COM_export_object(obj TSRMLS_CC);
-#else
-
+ /* This object represents PHP global namespace */
disp = (IDispatch*) new ScriptDispatch;
-#endif
- trace("%08x: GetScriptDispatch(NULL --> %08x)\n", this, disp);
}
if (disp) {
- trace("--- Marshaling to stream\n");
- ret = CoMarshalInterThreadInterfaceInStream(IID_IDispatch, disp, &info->dispatch);
+ ret = GIT_put(disp, IID_IDispatch, &info->dispatch);
disp->Release();
} else {
ret = S_FALSE;
TWideString name(info->pstrName);
IDispatch *disp;
- if (SUCCEEDED(CoGetInterfaceAndReleaseStream(info->marshal, IID_IDispatch, (void**)&disp)))
+ if (SUCCEEDED(GIT_get(info->marshal, IID_IDispatch, (void**)&disp)))
add_to_global_namespace(disp, info->dwFlags, name.ansi_string() TSRMLS_CC);
}
break;
case PHPSE_SET_SITE:
{
- LPSTREAM stream = (LPSTREAM)lParam;
-
if (m_pass_eng) {
m_pass_eng->Release();
m_pass_eng = NULL;
}
- if (stream)
- CoGetInterfaceAndReleaseStream(stream, IID_IActiveScriptSite, (void**)&m_pass_eng);
+ if (lParam)
+ GIT_get(lParam, IID_IActiveScriptSite, (void**)&m_pass_eng);
trace("%08x: site (engine-side) is now %08x (base=%08x)\n", this, m_pass_eng, m_pass);
* execute the opcodes */
struct php_active_script_parse_proc_info *info = (struct php_active_script_parse_proc_info*)lParam;
TWideString
- code(info->pstrCode),
- formal_params(info->pstrFormalParams),
- procedure_name(info->pstrProcedureName),
- item_name(info->pstrItemName),
- delimiter(info->pstrDelimiter);
+ formal_params(info->pstrFormalParams),
+ procedure_name(info->pstrProcedureName),
+ item_name(info->pstrItemName),
+ delimiter(info->pstrDelimiter);
- trace("%08x: ParseProc:\n state=%s\ncode=%s\n params=%s\nproc=%s\nitem=%s\n delim=%s\n line=%d\n",
+ trace("%08x: ParseProc:\n state=%s\nparams=%s\nproc=%s\nitem=%s\n delim=%s\n line=%d\n",
this, scriptstate_to_string(m_scriptstate),
- code.safe_ansi_string(), formal_params.ansi_string(), procedure_name.ansi_string(),
+ formal_params.ansi_string(), procedure_name.ansi_string(),
item_name.safe_ansi_string(), delimiter.safe_ansi_string(),
info->ulStartingLineNumber);
ScriptProcedureDispatch *disp = new ScriptProcedureDispatch;
- disp->AddRef();
disp->m_frag = frag;
disp->m_procflags = info->dwFlags;
disp->m_engine = this;
frag->ptr = disp;
+ info->dispcookie = disp->m_gitcookie;
- *info->ppdisp = disp;
- /*
- ret = CoMarshalInterThreadInterfaceInStream(IID_IDispatch, disp, &info->disp);
- disp->Release();
- */
} else {
ret = DISP_E_EXCEPTION;
}
if (msg.message == WM_QUIT) {
terminated = 1;
- } else {
+ } else if (msg.hwnd) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ } else {
handled = 1;
m_sync_thread_ret = engine_thread_handler(msg.message, msg.wParam, msg.lParam, &handled TSRMLS_CC);
if (handled)
if (m_pass) {
m_pass->AddRef();
- LPSTREAM stream;
- if (SUCCEEDED(CoMarshalInterThreadInterfaceInStream(IID_IActiveScriptSite, m_pass, &stream)))
- SEND_THREAD_MESSAGE(this, PHPSE_SET_SITE, 0, (LPARAM)stream TSRMLS_CC);
+ DWORD cookie;
+ if (SUCCEEDED(GIT_put(m_pass, IID_IActiveScriptSite, &cookie)))
+ SEND_THREAD_MESSAGE(this, PHPSE_SET_SITE, 0, cookie TSRMLS_CC);
if (m_scriptstate == SCRIPTSTATE_UNINITIALIZED)
SEND_THREAD_MESSAGE(this, PHPSE_STATE_CHANGE, 0, SCRIPTSTATE_INITIALIZED TSRMLS_CC);
info.dwFlags = dwFlags;
m_pass->GetItemInfo(pstrName, SCRIPTINFO_IUNKNOWN, &info.punk, NULL);
- if (SUCCEEDED(CoMarshalInterThreadInterfaceInStream(IID_IDispatch, info.punk, &info.marshal))) {
+ if (SUCCEEDED(GIT_put(info.punk, IID_IDispatch, &info.marshal))) {
SEND_THREAD_MESSAGE(this, PHPSE_ADD_NAMED_ITEM, 0, (LPARAM)&info TSRMLS_CC);
}
info.punk->Release();
}
if (S_OK == engine_thread_handler(PHPSE_GET_DISPATCH, 0, (LPARAM)&info, NULL TSRMLS_CC)) {
- CoGetInterfaceAndReleaseStream(info.dispatch, IID_IDispatch, (void**)ppdisp);
+ GIT_get(info.dispatch, IID_IDispatch, (void**)ppdisp);
}
if (*ppdisp) {
info.dwSourceContextCookie = dwSourceContextCookie;
info.ulStartingLineNumber = ulStartingLineNumber;
info.dwFlags = dwFlags;
- info.ppdisp = ppdisp;
ret = SEND_THREAD_MESSAGE(this, PHPSE_PARSE_PROC, 0, (LPARAM)&info TSRMLS_CC);
- /*
- if (ret == S_OK) {
- ret = CoGetInterfaceAndReleaseStream(info.disp, IID_IDispatch, (void**)ppdisp);
-
- }
- */
+ if (ret == S_OK)
+ ret = GIT_get(info.dispcookie, IID_IDispatch, (void**)ppdisp);
+
trace("ParseProc: ret=%08x disp=%08x\n", ret, *ppdisp);
return ret;
}