11#ifndef ASYNCMESSAGEWORKER_H
22#define ASYNCMESSAGEWORKER_H
33#include < nan.h>
4+ #include < cassert>
45#include < list>
6+ #include < unordered_map>
57#include " messages.h"
8+ #include " node_php_jsobject_class.h"
69
710namespace node_php_embed {
811
@@ -11,42 +14,117 @@ namespace node_php_embed {
1114 */
1215/* abstract */ class AsyncMessageWorker : public Nan ::AsyncWorker {
1316 public:
14- explicit AsyncMessageWorker (Nan::Callback *callback_)
15- : AsyncWorker(callback_), asyncdata_(), waitingForLock_(false ),
16- nextId_(0 ) {
17+ class MessageChannel ;
18+ explicit AsyncMessageWorker (Nan::Callback *callback)
19+ : AsyncWorker(callback), asyncdata_(), waitingForLock_(false ),
20+ phpObjToId_(), phpObjList_(),
21+ // id #0 is reserved for "invalid object"
22+ nextId_(1 ) {
1723 async = new uv_async_t ;
1824 uv_async_init (uv_default_loop (), async, AsyncMessage_);
1925 async->data = this ;
2026
21- uv_mutex_init (&async_lock);
27+ uv_mutex_init (&async_lock_);
28+ uv_mutex_init (&id_lock_);
2229
23- objToId_ .Reset (v8::NativeWeakMap::New (v8::Isolate::GetCurrent ()));
30+ jsObjToId_ .Reset (v8::NativeWeakMap::New (v8::Isolate::GetCurrent ()));
2431 }
2532
2633 virtual ~AsyncMessageWorker () {
27- uv_mutex_destroy (&async_lock);
34+ // invalidate any lingering js wrapper objects to this request
35+ ClearAllJsIds ();
36+ uv_mutex_destroy (&async_lock_);
37+ uv_mutex_destroy (&id_lock_);
2838 // can't safely delete entries from asyncdata_, it better be empty.
29- objToId_ .Reset ();
39+ jsObjToId_ .Reset ();
3040 }
3141
32- // Map Js object to an index
33- uint32_t IdForJsObj (const v8::Local<v8::Object> o) {
42+ // Map Js object to an index (JS thread only)
43+ objid_t IdForJsObj (const v8::Local<v8::Object> o) {
3444 // Have we already mapped this?
3545 Nan::HandleScope scope;
36- v8::Local<v8::NativeWeakMap> objToId = Nan::New (objToId_);
37- if (objToId->Has (o)) {
38- return Nan::To<uint32_t >(objToId->Get (o)).FromJust ();
46+ v8::Local<v8::NativeWeakMap> jsObjToId = Nan::New (jsObjToId_);
47+ if (jsObjToId->Has (o)) {
48+ return Nan::To<objid_t >(jsObjToId->Get (o)).FromJust ();
49+ }
50+ uv_mutex_lock (&id_lock_);
51+ objid_t id = (nextId_++);
52+ uv_mutex_unlock (&id_lock_);
53+ jsObjToId->Set (o, Nan::New (id));
54+ SaveToPersistent (id, o);
55+ return id;
56+ }
57+ v8::Local<v8::Object> JsObjForId (objid_t id) {
58+ Nan::EscapableHandleScope scope;
59+ // XXX if doesn't exist, then make a wrapper (and store it in the maps)
60+ return scope.Escape (Nan::To<v8::Object>(
61+ GetFromPersistent (id)
62+ ).ToLocalChecked ());
63+ }
64+ // Map PHP object to an index (PHP thread only)
65+ objid_t IdForPhpObj (zval *z) {
66+ if (phpObjToId_.count (z)) {
67+ return phpObjToId_.at (z);
68+ }
69+ uv_mutex_lock (&id_lock_);
70+ objid_t id = (nextId_++);
71+ uv_mutex_unlock (&id_lock_);
72+ Z_ADDREF_P (z);
73+ if (id >= phpObjList_.size ()) { phpObjList_.resize (id+1 ); }
74+ phpObjList_[id] = z;
75+ phpObjToId_[z] = id;
76+ return id;
77+ }
78+ // returned value is owned by objectmapper, caller should not release it.
79+ zval * PhpObjForId (MessageChannel *channel, objid_t id TSRMLS_DC) {
80+ if (id >= phpObjList_.size ()) { phpObjList_.resize (id+1 ); }
81+ ZVal z (phpObjList_[id] ZEND_FILE_LINE_CC);
82+ if (z.IsNull ()) {
83+ node_php_jsobject_create (z.Ptr (), channel, id TSRMLS_CC);
84+ phpObjList_[id] = z.Ptr ();
85+ phpObjToId_[z.Ptr ()] = id;
86+ // one excess reference: owned by objectmapper
87+ return z.Escape ();
88+ }
89+ // don't increment reference
90+ return z.Ptr ();
91+ }
92+ // Free PHP references associated with an id (from PHP thread)
93+ void ClearPhpId (objid_t id) {
94+ zval *z = (id < phpObjList_.size ()) ? phpObjList_[id] : NULL ;
95+ if (z) {
96+ phpObjList_[id] = NULL ;
97+ phpObjToId_.erase (z);
98+ zval_ptr_dtor (&z);
99+ }
100+ }
101+ void ClearAllPhpIds () {
102+ for (objid_t id = 1 ; id < nextId_; id++) {
103+ ClearPhpId (id);
104+ }
105+ }
106+ // Free JS references associated with an id (from JS thread)
107+ void ClearJsId (objid_t id) {
108+ Nan::HandleScope scope;
109+ Nan::MaybeLocal<v8::Object> o =
110+ Nan::To<v8::Object>(GetFromPersistent (id));
111+ if (o.IsEmpty ()) { return ; }
112+ // XXX depending on o's type, set its "is request valid" flag to false
113+ // since there might be other references to this object.
114+ v8::Local<v8::NativeWeakMap> jsObjToId = Nan::New (jsObjToId_);
115+ jsObjToId->Delete (o.ToLocalChecked ());
116+ SaveToPersistent (id, Nan::Undefined ());
117+ }
118+ void ClearAllJsIds () {
119+ for (objid_t id = 1 ; id < nextId_; id++) {
120+ ClearJsId (id);
39121 }
40- uint32_t r = (nextId_++);
41- objToId->Set (o, Nan::New (r));
42- SaveToPersistent (r, o);
43- return r;
44122 }
45123
46124 void WorkComplete () {
47- uv_mutex_lock (&async_lock );
125+ uv_mutex_lock (&async_lock_ );
48126 waitingForLock_ = !asyncdata_.empty ();
49- uv_mutex_unlock (&async_lock );
127+ uv_mutex_unlock (&async_lock_ );
50128
51129 if (!waitingForLock_) {
52130 Nan::AsyncWorker::WorkComplete ();
@@ -62,10 +140,10 @@ namespace node_php_embed {
62140 std::list<MessageToJs *> newData;
63141 bool waiting;
64142
65- uv_mutex_lock (&async_lock );
143+ uv_mutex_lock (&async_lock_ );
66144 newData.splice (newData.begin (), asyncdata_);
67145 waiting = waitingForLock_;
68- uv_mutex_unlock (&async_lock );
146+ uv_mutex_unlock (&async_lock_ );
69147
70148 for (std::list<MessageToJs *>::iterator it = newData.begin ();
71149 it != newData.end (); it++) {
@@ -91,20 +169,17 @@ namespace node_php_embed {
91169 AsyncMessageWorker *GetWorker () const {
92170 return that_;
93171 }
94- virtual uint32_t IdForJsObj (const v8::Local<v8::Object> o) {
172+ virtual objid_t IdForJsObj (const v8::Local<v8::Object> o) {
95173 return that_->IdForJsObj (o);
96174 }
97- virtual v8::Local<v8::Object> GetJs (uint32_t id) {
98- Nan::EscapableHandleScope scope;
99- return scope.Escape (Nan::To<v8::Object>(
100- that_->GetFromPersistent (id)
101- ).ToLocalChecked ());
175+ virtual v8::Local<v8::Object> JsObjForId (objid_t id) {
176+ return that_->JsObjForId (id);
102177 }
103- virtual uint32_t IdForPhpObj (const zval *o) {
104- return 0 ; // XXX
178+ virtual objid_t IdForPhpObj (zval *o) {
179+ return that_-> IdForPhpObj (o);
105180 }
106- virtual void GetPhp ( uint32_t id, zval *return_value TSRMLS_DC) {
107- // XXX use GetFromPersistent, and then unwrap v8::External?
181+ virtual zval * PhpObjForId ( objid_t id TSRMLS_DC) {
182+ return that_-> PhpObjForId ( this , id TSRMLS_CC);
108183 }
109184 private:
110185 explicit MessageChannel (AsyncMessageWorker* that) : that_(that) {}
@@ -123,16 +198,37 @@ namespace node_php_embed {
123198 * WorkComplete */
124199 }
125200
201+ // Limited ObjectMapper for use during subclass initialization.
202+ protected:
203+ class JsOnlyMapper : public ObjectMapper {
204+ public:
205+ JsOnlyMapper (AsyncMessageWorker *worker) : worker_(worker) { }
206+ virtual objid_t IdForJsObj (const v8::Local<v8::Object> o) {
207+ return worker_->IdForJsObj (o);
208+ }
209+ virtual v8::Local<v8::Object> JsObjForId (objid_t id) {
210+ assert (false ); return Nan::New<v8::Object>();
211+ }
212+ virtual objid_t IdForPhpObj (zval *o) {
213+ assert (false ); return 0 ;
214+ }
215+ virtual zval * PhpObjForId (objid_t id TSRMLS_DC) {
216+ assert (false ); return NULL ;
217+ }
218+ private:
219+ AsyncMessageWorker *worker_;
220+ };
221+
126222 private:
127223 void Execute () /* final override*/ {
128224 MessageChannel messageChannel (this );
129225 Execute (messageChannel);
130226 }
131227
132228 void SendMessage_ (MessageToJs *b) {
133- uv_mutex_lock (&async_lock );
229+ uv_mutex_lock (&async_lock_ );
134230 asyncdata_.push_back (b);
135- uv_mutex_unlock (&async_lock );
231+ uv_mutex_unlock (&async_lock_ );
136232
137233 uv_async_send (async);
138234 }
@@ -151,12 +247,19 @@ namespace node_php_embed {
151247 }
152248
153249 uv_async_t *async;
154- uv_mutex_t async_lock ;
250+ uv_mutex_t async_lock_ ;
155251 std::list<MessageToJs *> asyncdata_;
156252 bool waitingForLock_;
157253 // Js Object mapping (along with GetFromPersistent/etc)
158- Nan::Persistent<v8::NativeWeakMap> objToId_;
159- uint32_t nextId_;
254+ // Read/writable only from Js thread
255+ Nan::Persistent<v8::NativeWeakMap> jsObjToId_;
256+ // PHP Object mapping
257+ // Read/writable only from PHP thread
258+ std::unordered_map<zval*,objid_t > phpObjToId_;
259+ std::vector<zval*> phpObjList_;
260+ // Ids are allocated from both threads, so mutex is required
261+ uv_mutex_t id_lock_;
262+ objid_t nextId_;
160263};
161264
162265}
0 commit comments