diff --git a/go.mod b/go.mod index 6e8ba29..efbe205 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,9 @@ module github.com/grexie/isolates go 1.17 -require github.com/grexie/refutils v0.1.1 +require ( + github.com/grexie/refutils v0.1.1 + github.com/huandu/go-tls v1.0.1 +) + +require golang.org/x/sys v0.0.0-20200107162124-548cf772de50 // indirect diff --git a/go.sum b/go.sum index 83b4973..4babf05 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,6 @@ github.com/grexie/refutils v0.1.1 h1:106Cml8XyqD9eiBNg9lNurvfe6IuvuydS1YJEc9cs4o= github.com/grexie/refutils v0.1.1/go.mod h1:jnl4Ft2YXrxwMKPiBrHYwMO6tAEtFzn6M0oHBEgBWRA= +github.com/huandu/go-tls v1.0.1 h1:vdRzdlUFlqXWy0cfy5lVvHgUf6rt8QwEA/gCOCzsDX8= +github.com/huandu/go-tls v1.0.1/go.mod h1:WeItecBdaIdUBRb7cSMMk+rq41iFKhf6Q9mDRDpbdec= +golang.org/x/sys v0.0.0-20200107162124-548cf772de50 h1:YvQ10rzcqWXLlJZ3XCUoO25savxmscf4+SC+ZqiCHhA= +golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/v8_callback.go b/v8_callback.go index 9565f83..dad466c 100644 --- a/v8_callback.go +++ b/v8_callback.go @@ -6,6 +6,7 @@ package isolates import "C" import ( + "context" "fmt" "runtime/debug" "strconv" @@ -22,66 +23,76 @@ type callbackArgs struct { Holder *Value } -func functionCallbackHandler(context *Context, info C.CallbackInfo, args callbackArgs, functionId refutils.ID) (*Value, error) { - functionRef := context.functions.Get(functionId) - if functionRef == nil { - panic(fmt.Errorf("missing function pointer during callback for function #%d", functionId)) - } - function := (functionRef.(*functionInfo)).Function +func functionCallbackHandler(ctx context.Context, v8Context *Context, info C.CallbackInfo, args callbackArgs, functionId refutils.ID) (*Value, error) { + return v8Context.isolate.Sync(ctx, func(ctx context.Context) (*Value, error) { + functionRef := v8Context.functions.Get(functionId) + if functionRef == nil { + panic(fmt.Errorf("missing function pointer during callback for function #%d", functionId)) + } + function := (functionRef.(*functionInfo)).Function - argc := int(info.argc) - pargv := (*[1 << (maxArraySize - 18)]C.ValueTuple)(unsafe.Pointer(info.argv))[:argc:argc] - argv := make([]*Value, argc) - for i := 0; i < argc; i++ { - argv[i] = context.newValue(pargv[i].value, pargv[i].kinds) - } + argc := int(info.argc) + pargv := (*[1 << (maxArraySize - 18)]C.ValueTuple)(unsafe.Pointer(info.argv))[:argc:argc] + argv := make([]*Value, argc) + for i := 0; i < argc; i++ { + argv[i] = v8Context.newValue(pargv[i].value, pargv[i].kinds) + } - return function(FunctionArgs{ - context, - args.Caller, - args.This, - args.Holder, - bool(info.isConstructCall), - argv, + return function(FunctionArgs{ + ctx, + v8Context, + args.Caller, + args.This, + args.Holder, + bool(info.isConstructCall), + argv, + }) }) + } -func getterCallbackHandler(context *Context, info C.CallbackInfo, args callbackArgs, accessorId refutils.ID) (*Value, error) { - accessorRef := context.accessors.Get(accessorId) - if accessorRef == nil { - panic(fmt.Errorf("missing function pointer during callback for getter #%d", accessorId)) - } - getter := (accessorRef.(*accessorInfo)).Getter - - return getter(GetterArgs{ - context, - args.Caller, - args.This, - args.Holder, - C.GoStringN(info.key.data, info.key.length), +func getterCallbackHandler(ctx context.Context, v8Context *Context, info C.CallbackInfo, args callbackArgs, accessorId refutils.ID) (*Value, error) { + return v8Context.isolate.Sync(ctx, func(ctx context.Context) (*Value, error) { + accessorRef := v8Context.accessors.Get(accessorId) + if accessorRef == nil { + panic(fmt.Errorf("missing function pointer during callback for getter #%d", accessorId)) + } + getter := (accessorRef.(*accessorInfo)).Getter + + return getter(GetterArgs{ + ctx, + v8Context, + args.Caller, + args.This, + args.Holder, + C.GoStringN(info.key.data, info.key.length), + }) }) } -func setterCallbackHandler(context *Context, info C.CallbackInfo, args callbackArgs, accessorId refutils.ID) (*Value, error) { - accessorRef := context.accessors.Get(accessorId) - if accessorRef == nil { - panic(fmt.Errorf("missing function pointer during callback for setter #%d", accessorId)) - } - setter := (accessorRef.(*accessorInfo)).Setter - - v := context.newValue(info.value.value, info.value.kinds) - - return nil, setter(SetterArgs{ - context, - args.Caller, - args.This, - args.Holder, - C.GoStringN(info.key.data, info.key.length), - v, +func setterCallbackHandler(ctx context.Context, v8Context *Context, info C.CallbackInfo, args callbackArgs, accessorId refutils.ID) (*Value, error) { + return v8Context.isolate.Sync(ctx, func(ctx context.Context) (*Value, error) { + accessorRef := v8Context.accessors.Get(accessorId) + if accessorRef == nil { + panic(fmt.Errorf("missing function pointer during callback for setter #%d", accessorId)) + } + setter := (accessorRef.(*accessorInfo)).Setter + + v := v8Context.newValue(info.value.value, info.value.kinds) + + return nil, setter(SetterArgs{ + ctx, + v8Context, + args.Caller, + args.This, + args.Holder, + C.GoStringN(info.key.data, info.key.length), + v, + }) }) } -var callbackHandlers = map[C.CallbackType]func(*Context, C.CallbackInfo, callbackArgs, refutils.ID) (*Value, error){ +var callbackHandlers = map[C.CallbackType]func(context.Context, *Context, C.CallbackInfo, callbackArgs, refutils.ID) (*Value, error){ C.kFunctionCallback: functionCallbackHandler, C.kGetterCallback: getterCallbackHandler, C.kSetterCallback: setterCallbackHandler, @@ -97,10 +108,11 @@ func callbackHandler(info *C.CallbackInfo) (r C.ValueTuple) { ids := C.GoStringN(info.id.data, info.id.length) - parts := strings.SplitN(ids, ":", 3) + parts := strings.SplitN(ids, ":", 4) isolateId, _ := strconv.Atoi(parts[0]) contextId, _ := strconv.Atoi(parts[1]) callbackId, _ := strconv.Atoi(parts[2]) + executionContextId, _ := strconv.Atoi(parts[3]) isolateRef := isolateRefs.Get(refutils.ID(isolateId)) if isolateRef == nil { @@ -112,7 +124,13 @@ func callbackHandler(info *C.CallbackInfo) (r C.ValueTuple) { if contextRef == nil { panic(fmt.Errorf("missing context pointer during callback for context #%d", contextId)) } - context := contextRef.(*Context) + v8Context := contextRef.(*Context) + + executionContextRef := executionContextRefs.Get(refutils.ID(executionContextId)) + if executionContextRef == nil { + panic(fmt.Errorf("missing execution context pointer during callback for execution context #%d", executionContextId)) + } + ctx := executionContextRef.(*ExecutionContext) defer func() { if v := recover(); v != nil { @@ -130,16 +148,16 @@ func callbackHandler(info *C.CallbackInfo) (r C.ValueTuple) { int(info.caller.column), } - self, _ := context.newValueFromTuple(info.self) - holder, _ := context.newValueFromTuple(info.holder) + self, _ := v8Context.newValueFromTuple(ctx.ctx, info.self) + holder, _ := v8Context.newValueFromTuple(ctx.ctx, info.holder) - args := callbackArgs{context, callerInfo, self, holder} - v, err := callbackHandlers[info._type](context, *info, args, refutils.ID(callbackId)) + args := callbackArgs{v8Context, callerInfo, self, holder} + v, err := callbackHandlers[info._type](ctx.ctx, v8Context, *info, args, refutils.ID(callbackId)) - if err := isolate.lock(); err != nil { + if locked, err := isolate.lock(ctx.ctx); err != nil { return C.ValueTuple{} - } else { - defer isolate.unlock() + } else if locked { + defer isolate.unlock(ctx.ctx) } if err != nil { @@ -150,7 +168,7 @@ func callbackHandler(info *C.CallbackInfo) (r C.ValueTuple) { if v == nil { return C.ValueTuple{} - } else if v.context.isolate.pointer != context.isolate.pointer { + } else if v.context.isolate.pointer != v8Context.isolate.pointer { m := fmt.Sprintf("callback returned a value from another isolate") cerr := C.Error{data: C.CString(m), length: C.int(len(m))} return C.ValueTuple{error: cerr} diff --git a/v8_context.go b/v8_context.go index 347ec5c..ff33b32 100644 --- a/v8_context.go +++ b/v8_context.go @@ -6,6 +6,7 @@ package isolates import "C" import ( + "context" "reflect" "runtime" "sync" @@ -40,11 +41,11 @@ type Context struct { weakCallbackMutex sync.Mutex } -func (i *Isolate) NewContext() (*Context, error) { - if err := i.lock(); err != nil { +func (i *Isolate) NewContext(ctx context.Context) (*Context, error) { + if locked, err := i.lock(ctx); err != nil { return nil, err - } else { - defer i.unlock() + } else if locked { + defer i.unlock(ctx) } context := &Context{ @@ -80,31 +81,51 @@ func (c *Context) unref() { c.isolate.contexts.Unref(c) } -func (c *Context) Run(code string, filename string) (*Value, error) { - if err := c.isolate.lock(); err != nil { - return nil, err - } else { - defer c.isolate.unlock() - } +func (c *Context) AddMicrotask(ctx context.Context, fn func(in FunctionArgs) error) error { + _, err := c.isolate.Sync(ctx, func(ctx context.Context) (*Value, error) { + wrapper := func(in FunctionArgs) (*Value, error) { + return nil, fn(in) + } + + if value, err := c.Create(ctx, wrapper); err != nil { + return nil, err + } else if err := c.isolate.EnqueueMicrotaskWithValue(ctx, value); err != nil { + return nil, err + } - pcode := C.CString(code) - pfilename := C.CString(filename) + return nil, nil + }) + + return err +} - c.ref() - vt := C.v8_Context_Run(c.pointer, pcode, pfilename) - c.unref() +func (c *Context) Run(ctx context.Context, code string, filename string) (*Value, error) { + return c.isolate.Sync(ctx, func(ctx context.Context) (*Value, error) { + if locked, err := c.isolate.lock(ctx); err != nil { + return nil, err + } else if locked { + defer c.isolate.unlock(ctx) + } - C.free(unsafe.Pointer(pcode)) - C.free(unsafe.Pointer(pfilename)) + pcode := C.CString(code) + pfilename := C.CString(filename) - return c.newValueFromTuple(vt) + c.ref() + vt := C.v8_Context_Run(c.pointer, pcode, pfilename) + c.unref() + + C.free(unsafe.Pointer(pcode)) + C.free(unsafe.Pointer(pfilename)) + + return c.newValueFromTuple(ctx, vt) + }) } -func (c *Context) Undefined() (*Value, error) { - if err := c.isolate.lock(); err != nil { +func (c *Context) Undefined(ctx context.Context) (*Value, error) { + if locked, err := c.isolate.lock(ctx); err != nil { return nil, err - } else { - defer c.isolate.unlock() + } else if locked { + defer c.isolate.unlock(ctx) } if c.undefined == nil { @@ -113,11 +134,11 @@ func (c *Context) Undefined() (*Value, error) { return c.undefined, nil } -func (c *Context) Null() (*Value, error) { - if err := c.isolate.lock(); err != nil { +func (c *Context) Null(ctx context.Context) (*Value, error) { + if locked, err := c.isolate.lock(ctx); err != nil { return nil, err - } else { - defer c.isolate.unlock() + } else if locked { + defer c.isolate.unlock(ctx) } if c.null == nil { @@ -126,11 +147,11 @@ func (c *Context) Null() (*Value, error) { return c.null, nil } -func (c *Context) False() (*Value, error) { - if err := c.isolate.lock(); err != nil { +func (c *Context) False(ctx context.Context) (*Value, error) { + if locked, err := c.isolate.lock(ctx); err != nil { return nil, err - } else { - defer c.isolate.unlock() + } else if locked { + defer c.isolate.unlock(ctx) } if c.vfalse == nil { @@ -139,11 +160,11 @@ func (c *Context) False() (*Value, error) { return c.vfalse, nil } -func (c *Context) True() (*Value, error) { - if err := c.isolate.lock(); err != nil { +func (c *Context) True(ctx context.Context) (*Value, error) { + if locked, err := c.isolate.lock(ctx); err != nil { return nil, err - } else { - defer c.isolate.unlock() + } else if locked { + defer c.isolate.unlock(ctx) } if c.vtrue == nil { @@ -152,11 +173,11 @@ func (c *Context) True() (*Value, error) { return c.vtrue, nil } -func (c *Context) Global() (*Value, error) { - if err := c.isolate.lock(); err != nil { +func (c *Context) Global(ctx context.Context) (*Value, error) { + if locked, err := c.isolate.lock(ctx); err != nil { return nil, err - } else { - defer c.isolate.unlock() + } else if locked { + defer c.isolate.unlock(ctx) } if c.global == nil { @@ -165,53 +186,57 @@ func (c *Context) Global() (*Value, error) { return c.global, nil } -func (c *Context) ParseJSON(json string) (*Value, error) { - if err := c.isolate.lock(); err != nil { +func (c *Context) ParseJSON(ctx context.Context, json string) (*Value, error) { + if locked, err := c.isolate.lock(ctx); err != nil { return nil, err - } else { - defer c.isolate.unlock() + } else if locked { + defer c.isolate.unlock(ctx) } pjson := C.CString(json) defer C.free(unsafe.Pointer(pjson)) - return c.newValueFromTuple(C.v8_JSON_Parse(c.pointer, pjson)) + return c.newValueFromTuple(ctx, C.v8_JSON_Parse(c.pointer, pjson)) } func (c *Context) release() { - runtime.SetFinalizer(c, nil) - - c.global = nil - c.undefined = nil - c.null = nil - c.vfalse = nil - c.vtrue = nil - - c.functions.ReleaseAll() - c.accessors.ReleaseAll() - c.values.ReleaseAll() - c.refs.ReleaseAll() - c.objects = nil - - c.baseConstructor = nil - c.constructors = nil - - c.weakCallbacks = nil - - tracer.RemoveRefMap("functionInfo", c.functions) - tracer.RemoveRefMap("accessorInfo", c.accessors) - tracer.RemoveRefMap("valueRef", c.values) - tracer.RemoveRefMap("refs", c.refs) - tracer.Remove(c) - - if err := c.isolate.lock(); err == nil { - defer c.isolate.unlock() - } - - if c.pointer != nil { - C.v8_Context_Release(c.pointer) - c.pointer = nil - } - - c.isolate.contexts.Release(c) - + ctx := WithContext(context.Background()) + c.isolate.Sync(ctx, func(ctx context.Context) (*Value, error) { + runtime.SetFinalizer(c, nil) + + c.global = nil + c.undefined = nil + c.null = nil + c.vfalse = nil + c.vtrue = nil + + c.functions.ReleaseAll() + c.accessors.ReleaseAll() + c.values.ReleaseAll() + c.refs.ReleaseAll() + c.objects = nil + + c.baseConstructor = nil + c.constructors = nil + + c.weakCallbacks = nil + + tracer.RemoveRefMap("functionInfo", c.functions) + tracer.RemoveRefMap("accessorInfo", c.accessors) + tracer.RemoveRefMap("valueRef", c.values) + tracer.RemoveRefMap("refs", c.refs) + tracer.Remove(c) + + if locked, _ := c.isolate.lock(ctx); locked { + defer c.isolate.unlock(ctx) + } + + if c.pointer != nil { + C.v8_Context_Release(c.pointer) + c.pointer = nil + } + + c.isolate.contexts.Release(c) + + return nil, nil + }) } diff --git a/v8_create.go b/v8_create.go index 7d1f7ef..5f556ae 100644 --- a/v8_create.go +++ b/v8_create.go @@ -6,6 +6,7 @@ package isolates import "C" import ( + "context" "fmt" "reflect" "runtime" @@ -77,26 +78,19 @@ func isZero(v reflect.Value) bool { return v.Interface() == z.Interface() } -func (c *Context) Create(v interface{}) (*Value, error) { - // c.isolate.lock() - c.objectsMutex.Lock() - c.weakCallbackMutex.Lock() - c.constructorsMutex.Lock() - defer c.constructorsMutex.Unlock() - defer c.weakCallbackMutex.Unlock() - defer c.objectsMutex.Unlock() - // defer c.isolate.unlock() - - rv := reflect.ValueOf(v) - value, err := c.create(rv) - return value, err +func (c *Context) Create(ctx context.Context, v interface{}) (*Value, error) { + return c.isolate.Sync(ctx, func(ctx context.Context) (*Value, error) { + rv := reflect.ValueOf(v) + value, err := c.create(ctx, rv) + return value, err + }) } -func (c *Context) createImmediateValue(v C.ImmediateValue, kinds kinds) (*Value, error) { - if err := c.isolate.lock(); err != nil { +func (c *Context) createImmediateValue(ctx context.Context, v C.ImmediateValue, kinds kinds) (*Value, error) { + if locked, err := c.isolate.lock(ctx); err != nil { return nil, err - } else { - defer c.isolate.unlock() + } else if locked { + defer c.isolate.unlock(ctx) } return c.newValue(C.v8_Context_Create(c.pointer, v), C.Kinds(kinds)), nil @@ -113,9 +107,15 @@ func marshalValue(val reflect.Value) reflect.Value { return val } -func (c *Context) create(v reflect.Value) (*Value, error) { +func (c *Context) create(ctx context.Context, v reflect.Value) (*Value, error) { + if locked, err := c.isolate.lock(ctx); err != nil { + return nil, err + } else if locked { + defer c.isolate.unlock(ctx) + } + if !v.IsValid() { - return c.Undefined() + return c.Undefined(ctx) } if v.Type() == valueType { @@ -125,13 +125,13 @@ func (c *Context) create(v reflect.Value) (*Value, error) { v = marshalValue(v) if v.Type() == errorType { - if global, err := c.Global(); err != nil { + if global, err := c.Global(ctx); err != nil { return nil, err - } else if errorClass, err := global.Get("Error"); err != nil { + } else if errorClass, err := global.Get(ctx, "Error"); err != nil { return nil, err - } else if message, err := c.Create(fmt.Sprintf("%v", v.Interface())); err != nil { + } else if message, err := c.create(ctx, reflect.ValueOf(fmt.Sprintf("%v", v.Interface()))); err != nil { return nil, err - } else if errorObject, err := errorClass.New(message); err != nil { + } else if errorObject, err := errorClass.New(ctx, message); err != nil { return nil, err } else { return errorObject, nil @@ -140,7 +140,7 @@ func (c *Context) create(v reflect.Value) (*Value, error) { if v.Type() == timeType { msec := C.double(v.Interface().(time.Time).UnixNano()) / 1e6 - return c.createImmediateValue(C.ImmediateValue{_type: C.tDATE, _float64: msec}, unionKindDate) + return c.createImmediateValue(ctx, C.ImmediateValue{_type: C.tDATE, _float64: msec}, unionKindDate) } switch v.Kind() { @@ -149,17 +149,17 @@ func (c *Context) create(v reflect.Value) (*Value, error) { if v.Bool() { b = C.bool(true) } - return c.createImmediateValue(C.ImmediateValue{_type: C.tBOOL, _bool: b}, mask(KindBoolean)) + return c.createImmediateValue(ctx, C.ImmediateValue{_type: C.tBOOL, _bool: b}, mask(KindBoolean)) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64: n := C.double(v.Convert(float64Type).Float()) - return c.createImmediateValue(C.ImmediateValue{_type: C.tFLOAT64, _float64: n}, mask(KindNumber)) + return c.createImmediateValue(ctx, C.ImmediateValue{_type: C.tFLOAT64, _float64: n}, mask(KindNumber)) case reflect.String: s := v.String() ps := C.ByteArray{data: C.CString(s), length: C.int(len(s))} defer C.free(unsafe.Pointer(ps.data)) - return c.createImmediateValue(C.ImmediateValue{_type: C.tSTRING, _data: ps}, unionKindString) + return c.createImmediateValue(ctx, C.ImmediateValue{_type: C.tSTRING, _data: ps}, unionKindString) case reflect.UnsafePointer, reflect.Uintptr: return nil, fmt.Errorf("uintptr not supported: %#v", v.Interface()) case reflect.Complex64, reflect.Complex128: @@ -168,35 +168,35 @@ func (c *Context) create(v reflect.Value) (*Value, error) { return nil, fmt.Errorf("chan not supported: %#v", v.Interface()) case reflect.Func: if v.Type().ConvertibleTo(functionType) { - if ft, err := c.NewFunctionTemplate(v.Convert(functionType).Interface().(Function)); err != nil { + if ft, err := c.NewFunctionTemplate(ctx, v.Convert(functionType).Interface().(Function)); err != nil { return nil, err } else { - ft.SetName(runtime.FuncForPC(uintptr(v.Pointer())).Name()) - return ft.GetFunction() + ft.SetName(ctx, runtime.FuncForPC(uintptr(v.Pointer())).Name()) + return ft.GetFunction(ctx) } } else if err := isConstructor(v.Type()); err == nil { - return c.createConstructor(v.Interface()) + return c.createConstructor(ctx, v.Interface()) } return nil, fmt.Errorf("func not supported: %#v", v.Interface()) case reflect.Interface, reflect.Ptr: - return c.create(v.Elem()) + return c.create(ctx, v.Elem()) case reflect.Map: if v.Type().Key() != stringType { return nil, fmt.Errorf("map keys must be strings, %s not permissable in v8", v.Type().Key()) } - if o, err := c.createImmediateValue(C.ImmediateValue{_type: C.tOBJECT}, mask(KindObject)); err != nil { + if o, err := c.createImmediateValue(ctx, C.ImmediateValue{_type: C.tOBJECT}, mask(KindObject)); err != nil { return nil, err } else { keys := v.MapKeys() sort.Sort(stringKeys(keys)) for _, k := range keys { - if vk, err := c.create(v.MapIndex(k)); err != nil { + if vk, err := c.create(ctx, v.MapIndex(k)); err != nil { return nil, fmt.Errorf("map key %q: %v", k.String(), err) - } else if err := o.Set(k.String(), vk); err != nil { + } else if err := o.Set(ctx, k.String(), vk); err != nil { return nil, err } else { - vk.release() + vk.releaseWithContext(ctx) } } @@ -205,15 +205,15 @@ func (c *Context) create(v reflect.Value) (*Value, error) { case reflect.Struct: if value, ok := c.objects[uintptr(v.Addr().Pointer())]; ok { return value, nil - } else if p, err := c.createPrototype(v, v.Type()); err != nil { + } else if p, err := c.createPrototype(ctx, v, v.Type()); err != nil { return nil, err - } else if fn, err := p.GetFunction(); err != nil { + } else if fn, err := p.GetFunction(ctx); err != nil { return nil, err - } else if value, err := fn.New(); err != nil { + } else if value, err := fn.New(ctx); err != nil { return nil, err } else { c.objects[uintptr(v.Addr().Pointer())] = value - value.SetReceiver(&v) + value.SetReceiver(ctx, &v) return value, nil } case reflect.Array, reflect.Slice: @@ -228,7 +228,7 @@ func (c *Context) create(v reflect.Value) (*Value, error) { pb = (*C.char)(unsafe.Pointer(&v.Bytes()[0])) } - return c.createImmediateValue( + return c.createImmediateValue(ctx, C.ImmediateValue{ _type: C.tARRAYBUFFER, _data: C.ByteArray{data: pb, length: C.int(v.Len())}, @@ -236,7 +236,7 @@ func (c *Context) create(v reflect.Value) (*Value, error) { unionKindArrayBuffer, ) } else { - if o, err := c.createImmediateValue( + if o, err := c.createImmediateValue(ctx, C.ImmediateValue{ _type: C.tARRAY, _data: C.ByteArray{data: nil, length: C.int(v.Len())}, @@ -246,12 +246,12 @@ func (c *Context) create(v reflect.Value) (*Value, error) { return nil, err } else { for i := 0; i < v.Len(); i++ { - if v, err := c.create(v.Index(i)); err != nil { + if v, err := c.create(ctx, v.Index(i)); err != nil { return nil, fmt.Errorf("index %d: %v", i, err) - } else if err := o.SetIndex(i, v); err != nil { + } else if err := o.SetIndex(ctx, i, v); err != nil { return nil, err } else { - v.release() + v.releaseWithContext(ctx) } } @@ -305,7 +305,7 @@ func isConstructor(constructor reflect.Type) error { return nil } -func (c *Context) createConstructor(cons interface{}) (*Value, error) { +func (c *Context) createConstructor(ctx context.Context, cons interface{}) (*Value, error) { cv := reflect.ValueOf(cons) constructor := cv.Type() prototype := constructor.Out(0) @@ -315,40 +315,40 @@ func (c *Context) createConstructor(cons interface{}) (*Value, error) { } if fn, ok := c.constructors[constructor]; ok { - return fn.GetFunction() - } else if pfn, err := c.createPrototype(reflect.Zero(prototype), prototype); err != nil { + return fn.GetFunction(ctx) + } else if pfn, err := c.createPrototype(ctx, reflect.Zero(prototype), prototype); err != nil { return nil, err - } else if pfnv, err := pfn.GetFunction(); err != nil { + } else if pfnv, err := pfn.GetFunction(ctx); err != nil { return nil, err - } else if fn, err := c.NewFunctionTemplate(func(in FunctionArgs) (*Value, error) { - pfnv.Call(in.This) + } else if fn, err := c.NewFunctionTemplate(ctx, func(in FunctionArgs) (*Value, error) { + pfnv.Call(in.ExecutionContext, in.This) r := cv.Call([]reflect.Value{reflect.ValueOf(in)}) if r[1].Interface() != nil { return nil, r[1].Interface().(error) } - in.This.SetReceiver(&r[0]) + in.This.SetReceiver(in.ExecutionContext, &r[0]) return in.This, nil }); err != nil { return nil, err - } else if err := fn.SetName(prototype.Name()); err != nil { + } else if err := fn.SetName(ctx, prototype.Name()); err != nil { return nil, err - } else if instance, err := fn.GetInstanceTemplate(); err != nil { + } else if instance, err := fn.GetInstanceTemplate(ctx); err != nil { return nil, err - } else if err := instance.SetInternalFieldCount(1); err != nil { + } else if err := instance.SetInternalFieldCount(ctx, 1); err != nil { return nil, err - } else if proto, err := fn.GetPrototypeTemplate(); err != nil { + } else if proto, err := fn.GetPrototypeTemplate(ctx); err != nil { return nil, err - } else if err := c.writePrototypeFields(instance, proto, reflect.Zero(prototype), prototype); err != nil { + } else if err := c.writePrototypeFields(ctx, instance, proto, reflect.Zero(prototype), prototype); err != nil { return nil, err } else { c.constructors[constructor] = fn - return fn.GetFunction() + return fn.GetFunction(ctx) } } -func (c *Context) createPrototype(v reflect.Value, prototype reflect.Type) (*FunctionTemplate, error) { +func (c *Context) createPrototype(ctx context.Context, v reflect.Value, prototype reflect.Type) (*FunctionTemplate, error) { if prototype.Kind() == reflect.Ptr || prototype.Kind() == reflect.Interface { prototype = prototype.Elem() } @@ -357,19 +357,19 @@ func (c *Context) createPrototype(v reflect.Value, prototype reflect.Type) (*Fun return fn, nil } else if prototype.Kind() != reflect.Interface && prototype.Kind() != reflect.Struct { return nil, fmt.Errorf("prototype must be an interface: %+v", prototype) - } else if fn, err := c.NewFunctionTemplate(func(in FunctionArgs) (*Value, error) { + } else if fn, err := c.NewFunctionTemplate(ctx, func(in FunctionArgs) (*Value, error) { return in.This, nil }); err != nil { return nil, err - } else if err := fn.SetName(prototype.Name()); err != nil { + } else if err := fn.SetName(ctx, prototype.Name()); err != nil { return nil, err - } else if instance, err := fn.GetInstanceTemplate(); err != nil { + } else if instance, err := fn.GetInstanceTemplate(ctx); err != nil { return nil, err - } else if err := instance.SetInternalFieldCount(1); err != nil { + } else if err := instance.SetInternalFieldCount(ctx, 1); err != nil { return nil, err - } else if proto, err := fn.GetPrototypeTemplate(); err != nil { + } else if proto, err := fn.GetPrototypeTemplate(ctx); err != nil { return nil, err - } else if err := c.writePrototypeFields(instance, proto, v, prototype); err != nil { + } else if err := c.writePrototypeFields(ctx, instance, proto, v, prototype); err != nil { return nil, err } else { c.constructors[prototype] = fn @@ -377,109 +377,111 @@ func (c *Context) createPrototype(v reflect.Value, prototype reflect.Type) (*Fun } } -func (c *Context) createFieldGetter(t reflect.Type, name string) Getter { +func (c *Context) createFieldGetter(ctx context.Context, t reflect.Type, name string) Getter { return func(in GetterArgs) (*Value, error) { - r := in.Holder.Receiver(t) - - if r == nil { + if r, err := in.Holder.Receiver(in.ExecutionContext, t); err != nil { + return nil, err + } else if r == nil { return nil, fmt.Errorf("receiver not found") - } - - for r.Kind() == reflect.Ptr && !r.IsNil() { - v := r.Elem() - r = &v - } - - fval := r.FieldByName(name) - if v, err := in.Context.create(fval); err != nil { - return nil, fmt.Errorf("field %q: %v", name, err) } else { - return v, nil + for r.Kind() == reflect.Ptr && !r.IsNil() { + v := r.Elem() + r = &v + } + + fval := r.FieldByName(name) + if v, err := in.Context.create(ctx, fval); err != nil { + return nil, fmt.Errorf("field %q: %v", name, err) + } else { + return v, nil + } } } } -func (c *Context) createFieldSetter(t reflect.Type, name string) Setter { +func (c *Context) createFieldSetter(ctx context.Context, t reflect.Type, name string) Setter { return func(in SetterArgs) error { - r := in.Holder.Receiver(t) - - if r == nil { - return fmt.Errorf("receiver not found") - } else if r.Kind() == reflect.Ptr { - v := r.Elem() - r = &v - } - - fval := r.FieldByName(name) - if v, err := in.Value.Unmarshal(fval.Type()); err != nil { + if r, err := in.Holder.Receiver(in.ExecutionContext, t); err != nil { return err + } else if r == nil { + return fmt.Errorf("receiver not found") } else { - fval.Set(*v) - return nil + if r.Kind() == reflect.Ptr { + v := r.Elem() + r = &v + } + + fval := r.FieldByName(name) + if v, err := in.Value.Unmarshal(ctx, fval.Type()); err != nil { + return err + } else { + fval.Set(*v) + return nil + } } } } -func (c *Context) createGetter(t reflect.Type, method reflect.Value) Getter { +func (c *Context) createGetter(ctx context.Context, t reflect.Type, method reflect.Value) Getter { return func(in GetterArgs) (*Value, error) { - r := in.Holder.Receiver(t) - - if r == nil { + if r, err := in.Holder.Receiver(in.ExecutionContext, t); err != nil { + return nil, err + } else if r == nil { return nil, fmt.Errorf("receiver not found") - } - - v := method.Call([]reflect.Value{*r, reflect.ValueOf(in)}) - - if v1, ok := v[1].Interface().(error); ok { - return nil, v1 - } else if v0, ok := v[0].Interface().(*Value); ok { - return v0, nil } else { - return nil, nil + v := method.Call([]reflect.Value{*r, reflect.ValueOf(in)}) + + if v1, ok := v[1].Interface().(error); ok { + return nil, v1 + } else if v0, ok := v[0].Interface().(*Value); ok { + return v0, nil + } else { + return nil, nil + } } } } -func (c *Context) createSetter(t reflect.Type, method reflect.Value) Setter { +func (c *Context) createSetter(ctx context.Context, t reflect.Type, method reflect.Value) Setter { return func(in SetterArgs) error { - r := in.Holder.Receiver(t) - - if r == nil { + if r, err := in.Holder.Receiver(in.ExecutionContext, t); err != nil { + return err + } else if r == nil { return fmt.Errorf("receiver not found") - } - - v := method.Call([]reflect.Value{*r, reflect.ValueOf(in)}) - - if v1, ok := v[1].Interface().(error); ok { - return v1 } else { - return nil + v := method.Call([]reflect.Value{*r, reflect.ValueOf(in)}) + + if v1, ok := v[1].Interface().(error); ok { + return v1 + } else { + return nil + } } } } -func (c *Context) createFunctionAccessor(t reflect.Type, method reflect.Value, name string) (Getter, error) { - if ft, err := c.NewFunctionTemplate(func(in FunctionArgs) (*Value, error) { - r := in.Holder.Receiver(t) - - if r == nil { +func (c *Context) createFunctionAccessor(ctx context.Context, t reflect.Type, method reflect.Value, name string) (Getter, error) { + if ft, err := c.NewFunctionTemplate(ctx, func(in FunctionArgs) (*Value, error) { + if r, err := in.Holder.Receiver(in.ExecutionContext, t); err != nil { + return nil, err + } else if r == nil { return nil, fmt.Errorf("receiver not found") - } - - v := method.Call([]reflect.Value{*r, reflect.ValueOf(in)}) - - if v1, ok := v[1].Interface().(error); ok { - return nil, v1 - } else if v0, ok := v[0].Interface().(*Value); ok { - return v0, nil } else { - return nil, nil + v := method.Call([]reflect.Value{*r, reflect.ValueOf(in)}) + + if v1, ok := v[1].Interface().(error); ok { + return nil, v1 + } else if v0, ok := v[0].Interface().(*Value); ok { + return v0, nil + } else { + return nil, nil + } } }); err != nil { return nil, err - } else if err := ft.SetName(name); err != nil { + } else if err := ft.SetName(ctx, name); err != nil { return nil, err - } else if fn, err := ft.GetFunction(); err != nil { + } else if fn, err := ft.GetFunction(ctx); err != nil { return nil, err } else { return func(in GetterArgs) (*Value, error) { @@ -488,7 +490,7 @@ func (c *Context) createFunctionAccessor(t reflect.Type, method reflect.Value, n } } -func (c *Context) writePrototypeFields(v *ObjectTemplate, o *ObjectTemplate, value reflect.Value, prototype reflect.Type) error { +func (c *Context) writePrototypeFields(ctx context.Context, v *ObjectTemplate, o *ObjectTemplate, value reflect.Value, prototype reflect.Type) error { getters := map[string]Getter{} setters := map[string]Setter{} @@ -504,7 +506,7 @@ func (c *Context) writePrototypeFields(v *ObjectTemplate, o *ObjectTemplate, val } if sub.Kind() == reflect.Struct { - if err := c.writePrototypeFields(v, o, value, sub.Type()); err != nil { + if err := c.writePrototypeFields(ctx, v, o, value, sub.Type()); err != nil { return fmt.Errorf("Writing embedded field %q: %v", f.Name, err) } continue @@ -521,8 +523,8 @@ func (c *Context) writePrototypeFields(v *ObjectTemplate, o *ObjectTemplate, val } t := value.Type() - getters[name] = c.createFieldGetter(t, f.Name) - setters[name] = c.createFieldSetter(prototype, f.Name) + getters[name] = c.createFieldGetter(ctx, t, f.Name) + setters[name] = c.createFieldSetter(ctx, prototype, f.Name) } } @@ -537,20 +539,20 @@ func (c *Context) writePrototypeFields(v *ObjectTemplate, o *ObjectTemplate, val if m.Type().ConvertibleTo(getterType) { if strings.HasPrefix(name, "V8Get") { name = getName(strings.TrimPrefix(name, "V8Get")) - getters[name] = c.createGetter(prototype, method.Func) + getters[name] = c.createGetter(ctx, prototype, method.Func) } } else if m.Type().ConvertibleTo(setterType) { if strings.HasPrefix(name, "V8Set") { name = getName(strings.TrimPrefix(name, "V8Set")) - setters[name] = c.createSetter(prototype, method.Func) + setters[name] = c.createSetter(ctx, prototype, method.Func) } } else if m.Type().ConvertibleTo(functionType) { if strings.HasPrefix(name, "V8Func") { name = getName(strings.TrimPrefix(name, "V8Func")) - if fn, err := c.createFunctionAccessor(prototype, method.Func, name); err != nil { + if fn, err := c.createFunctionAccessor(ctx, prototype, method.Func, name); err != nil { return err } else { - o.SetAccessor(name, fn, nil) + o.SetAccessor(ctx, name, fn, nil) } } } @@ -558,12 +560,12 @@ func (c *Context) writePrototypeFields(v *ObjectTemplate, o *ObjectTemplate, val for name, getter := range getters { setter, _ := setters[name] - v.SetAccessor(name, getter, setter) + v.SetAccessor(ctx, name, getter, setter) } // Also export any methods of the struct pointer that match the callback type. if prototype.Kind() != reflect.Ptr { - return c.writePrototypeFields(v, o, value, reflect.PtrTo(prototype)) + return c.writePrototypeFields(ctx, v, o, value, reflect.PtrTo(prototype)) } return nil diff --git a/v8_function.go b/v8_function.go index bb9c94c..c62c291 100644 --- a/v8_function.go +++ b/v8_function.go @@ -6,6 +6,7 @@ package isolates import "C" import ( + "context" "fmt" "reflect" "runtime" @@ -42,19 +43,20 @@ type Getter func(GetterArgs) (*Value, error) type Setter func(SetterArgs) error type FunctionArgs struct { - Context *Context - Caller CallerInfo - This *Value - Holder *Value - IsConstructCall bool - Args []*Value + ExecutionContext context.Context + Context *Context + Caller CallerInfo + This *Value + Holder *Value + IsConstructCall bool + Args []*Value } -func (c *FunctionArgs) Arg(n int) *Value { +func (c *FunctionArgs) Arg(ctx context.Context, n int) *Value { if n < len(c.Args) && n >= 0 { return c.Args[n] } - if undefined, err := c.Context.Undefined(); err != nil { + if undefined, err := c.Context.Undefined(ctx); err != nil { // a panic should be ok here as it will be recovered in CallbackHandler // unless FunctionArgs has been passed to a goroutine panic(err) @@ -64,20 +66,22 @@ func (c *FunctionArgs) Arg(n int) *Value { } type GetterArgs struct { - Context *Context - Caller CallerInfo - This *Value - Holder *Value - Key string + ExecutionContext context.Context + Context *Context + Caller CallerInfo + This *Value + Holder *Value + Key string } type SetterArgs struct { - Context *Context - Caller CallerInfo - This *Value - Holder *Value - Key string - Value *Value + ExecutionContext context.Context + Context *Context + Caller CallerInfo + This *Value + Holder *Value + Key string + Value *Value } type functionInfo struct { @@ -98,11 +102,11 @@ type accessorInfo struct { Setter } -func (c *Context) NewFunctionTemplate(cb Function) (*FunctionTemplate, error) { - if err := c.isolate.lock(); err != nil { +func (c *Context) NewFunctionTemplate(ctx context.Context, cb Function) (*FunctionTemplate, error) { + if locked, err := c.isolate.lock(ctx); err != nil { return nil, err - } else { - defer c.isolate.unlock() + } else if locked { + defer c.isolate.unlock(ctx) } iid := c.isolate.ref() @@ -111,11 +115,14 @@ func (c *Context) NewFunctionTemplate(cb Function) (*FunctionTemplate, error) { cid := c.ref() defer c.unref() + ecid := FromContext(ctx).ref() + defer FromContext(ctx).unref() + info := &functionInfo{ Function: cb, } id := c.functions.Ref(info) - pid := C.CString(fmt.Sprintf("%d:%d:%d", iid, cid, id)) + pid := C.CString(fmt.Sprintf("%d:%d:%d:%d", iid, cid, id, ecid)) defer C.free(unsafe.Pointer(pid)) pf := C.v8_FunctionTemplate_New(c.pointer, pid) @@ -130,11 +137,11 @@ func (c *Context) NewFunctionTemplate(cb Function) (*FunctionTemplate, error) { return f, nil } -func (f *FunctionTemplate) Inherit(parent *FunctionTemplate) error { - if err := f.context.isolate.lock(); err != nil { +func (f *FunctionTemplate) Inherit(ctx context.Context, parent *FunctionTemplate) error { + if locked, err := f.context.isolate.lock(ctx); err != nil { return err - } else { - defer f.context.isolate.unlock() + } else if locked { + defer f.context.isolate.unlock(ctx) } f.context.ref() @@ -144,11 +151,11 @@ func (f *FunctionTemplate) Inherit(parent *FunctionTemplate) error { return nil } -func (f *FunctionTemplate) SetName(name string) error { - if err := f.context.isolate.lock(); err != nil { +func (f *FunctionTemplate) SetName(ctx context.Context, name string) error { + if locked, err := f.context.isolate.lock(ctx); err != nil { return err - } else { - defer f.context.isolate.unlock() + } else if locked { + defer f.context.isolate.unlock(ctx) } pname := C.CString(name) @@ -161,11 +168,11 @@ func (f *FunctionTemplate) SetName(name string) error { return nil } -func (f *FunctionTemplate) SetHiddenPrototype(value bool) error { - if err := f.context.isolate.lock(); err != nil { +func (f *FunctionTemplate) SetHiddenPrototype(ctx context.Context, value bool) error { + if locked, err := f.context.isolate.lock(ctx); err != nil { return err - } else { - defer f.context.isolate.unlock() + } else if locked { + defer f.context.isolate.unlock(ctx) } f.context.ref() @@ -175,11 +182,11 @@ func (f *FunctionTemplate) SetHiddenPrototype(value bool) error { return nil } -func (f *FunctionTemplate) GetFunction() (*Value, error) { - if err := f.context.isolate.lock(); err != nil { +func (f *FunctionTemplate) GetFunction(ctx context.Context) (*Value, error) { + if locked, err := f.context.isolate.lock(ctx); err != nil { return nil, err - } else { - defer f.context.isolate.unlock() + } else if locked { + defer f.context.isolate.unlock(ctx) } if f.value == nil { @@ -197,11 +204,11 @@ func (f *FunctionTemplate) GetFunction() (*Value, error) { return f.value, nil } -func (f *FunctionTemplate) GetInstanceTemplate() (*ObjectTemplate, error) { - if err := f.context.isolate.lock(); err != nil { +func (f *FunctionTemplate) GetInstanceTemplate(ctx context.Context) (*ObjectTemplate, error) { + if locked, err := f.context.isolate.lock(ctx); err != nil { return nil, err - } else { - defer f.context.isolate.unlock() + } else if locked { + defer f.context.isolate.unlock(ctx) } f.context.ref() @@ -217,11 +224,11 @@ func (f *FunctionTemplate) GetInstanceTemplate() (*ObjectTemplate, error) { return ot, nil } -func (f *FunctionTemplate) GetPrototypeTemplate() (*ObjectTemplate, error) { - if err := f.context.isolate.lock(); err != nil { +func (f *FunctionTemplate) GetPrototypeTemplate(ctx context.Context) (*ObjectTemplate, error) { + if locked, err := f.context.isolate.lock(ctx); err != nil { return nil, err - } else { - defer f.context.isolate.unlock() + } else if locked { + defer f.context.isolate.unlock(ctx) } f.context.ref() @@ -238,29 +245,33 @@ func (f *FunctionTemplate) GetPrototypeTemplate() (*ObjectTemplate, error) { } func (f *FunctionTemplate) release() { - tracer.Remove(f) - runtime.SetFinalizer(f, nil) - f.info = nil - f.value = nil - - if err := f.context.isolate.lock(); err == nil { - defer f.context.isolate.unlock() - } - - if f.context.pointer != nil { - C.v8_FunctionTemplate_Release(f.context.pointer, f.pointer) - } - - f.context = nil - f.pointer = nil + ctx := WithContext(context.Background()) + f.context.isolate.Sync(ctx, func(ctx context.Context) (*Value, error) { + tracer.Remove(f) + runtime.SetFinalizer(f, nil) + f.info = nil + f.value = nil + + if locked, _ := f.context.isolate.lock(ctx); locked { + defer f.context.isolate.unlock(ctx) + } + + if f.context.pointer != nil { + C.v8_FunctionTemplate_Release(f.context.pointer, f.pointer) + } + + f.context = nil + f.pointer = nil + return nil, nil + }) } -func (o *ObjectTemplate) SetInternalFieldCount(count int) error { - if err := o.context.isolate.lock(); err != nil { +func (o *ObjectTemplate) SetInternalFieldCount(ctx context.Context, count int) error { + if locked, err := o.context.isolate.lock(ctx); err != nil { return err - } else { - defer o.context.isolate.unlock() + } else if locked { + defer o.context.isolate.unlock(ctx) } o.context.ref() @@ -270,11 +281,11 @@ func (o *ObjectTemplate) SetInternalFieldCount(count int) error { return nil } -func (o *ObjectTemplate) SetAccessor(name string, getter Getter, setter Setter) error { - if err := o.context.isolate.lock(); err != nil { +func (o *ObjectTemplate) SetAccessor(ctx context.Context, name string, getter Getter, setter Setter) error { + if locked, err := o.context.isolate.lock(ctx); err != nil { return err - } else { - defer o.context.isolate.unlock() + } else if locked { + defer o.context.isolate.unlock(ctx) } iid := o.context.isolate.ref() @@ -283,11 +294,15 @@ func (o *ObjectTemplate) SetAccessor(name string, getter Getter, setter Setter) cid := o.context.ref() defer o.context.unref() + ecid := FromContext(ctx).ref() + defer FromContext(ctx).unref() + id := o.context.accessors.Ref(&accessorInfo{ Getter: getter, Setter: setter, }) - pid := C.CString(fmt.Sprintf("%d:%d:%d", iid, cid, id)) + + pid := C.CString(fmt.Sprintf("%d:%d:%d:%d", iid, cid, id, ecid)) defer C.free(unsafe.Pointer(pid)) pname := C.CString(name) @@ -298,18 +313,21 @@ func (o *ObjectTemplate) SetAccessor(name string, getter Getter, setter Setter) } func (o *ObjectTemplate) release() { - tracer.Remove(o) - runtime.SetFinalizer(o, nil) - - if err := o.context.isolate.lock(); err == nil { - defer o.context.isolate.unlock() - } - - if o.context.pointer != nil { - C.v8_ObjectTemplate_Release(o.context.pointer, o.pointer) - } - - o.context = nil - o.pointer = nil - + ctx := WithContext(context.Background()) + o.context.isolate.Sync(ctx, func(ctx context.Context) (*Value, error) { + tracer.Remove(o) + runtime.SetFinalizer(o, nil) + + if locked, _ := o.context.isolate.lock(ctx); locked { + defer o.context.isolate.unlock(ctx) + } + + if o.context.pointer != nil { + C.v8_ObjectTemplate_Release(o.context.pointer, o.pointer) + } + + o.context = nil + o.pointer = nil + return nil, nil + }) } diff --git a/v8_isolate.go b/v8_isolate.go index 48f7898..9c6d20d 100644 --- a/v8_isolate.go +++ b/v8_isolate.go @@ -6,24 +6,114 @@ package isolates import "C" import ( + "context" "errors" "fmt" + "time" + "reflect" "runtime" + "runtime/debug" + "sync" "unsafe" refutils "github.com/grexie/refutils" ) +const contextKey = "github.com/grexie/isolates" + +type ExecutionContext struct { + refutils.RefHolder + ctx context.Context + mutex sync.Mutex + locked bool + isolate *Isolate + entrantMutex sync.Mutex + enterCallbacks []func() + exitCallbacks []func() + enterSnapshot HeapStatistics +} + +func newIsolateContext(isolate *Isolate) *ExecutionContext { + context := &ExecutionContext{isolate: isolate, enterCallbacks: []func(){}, exitCallbacks: []func(){}} + context.ref() + return context +} + +func WithContext(ctx context.Context) context.Context { + executionContext := newIsolateContext(nil) + ctx = context.WithValue(ctx, contextKey, executionContext) + executionContext.ctx = ctx + return ctx +} + +func withIsolateContext(ctx context.Context, isolate *Isolate) context.Context { + executionContext := newIsolateContext(isolate) + ctx = context.WithValue(ctx, contextKey, executionContext) + executionContext.ctx = ctx + return ctx +} + +func FromContext(ctx context.Context) *ExecutionContext { + return ctx.Value(contextKey).(*ExecutionContext) +} + +func (ec *ExecutionContext) ref() refutils.ID { + + return executionContextRefs.Ref(ec) +} + +func (ec *ExecutionContext) unref() { + executionContextRefs.Unref(ec) +} + +func (ec *ExecutionContext) GetIsolate() *Isolate { + return ec.isolate +} + +func (ec *ExecutionContext) AddExecutionEnterCallback(callback func()) { + ec.enterCallbacks = append(ec.enterCallbacks, callback) +} + +func (ec *ExecutionContext) AddExecutionExitCallback(callback func()) { + ec.exitCallbacks = append(ec.exitCallbacks, callback) +} + +func (ec *ExecutionContext) enter() { + for _, callback := range ec.enterCallbacks { + callback() + } +} + +func (ec *ExecutionContext) exit() { + for _, callback := range ec.exitCallbacks { + callback() + } +} + +type callbackResult struct { + value *Value + err error +} +type callbackInfo struct { + fn func(context.Context) (*Value, error) + result chan callbackResult +} + type Isolate struct { refutils.RefHolder - pointer C.IsolatePtr - contexts *refutils.RefMap - mutex refutils.RefMutex - running bool - data map[string]interface{} - shutdownHooks []interface{} + pointer C.IsolatePtr + contexts *refutils.RefMap + mutex sync.Mutex + running bool + data map[string]interface{} + shutdownHooks []interface{} + lockerStackTrace []byte + + executionContext context.Context + callbacks chan callbackInfo + close chan bool } type Snapshot struct { @@ -43,6 +133,7 @@ type HeapStatistics struct { } var isolateRefs = refutils.NewWeakRefMap("i") +var executionContextRefs = refutils.NewWeakRefMap("ec") func NewIsolate() *Isolate { Initialize() @@ -53,12 +144,18 @@ func NewIsolate() *Isolate { running: true, data: map[string]interface{}{}, shutdownHooks: []interface{}{}, + callbacks: make(chan callbackInfo), + close: make(chan bool), } + isolate.executionContext = withIsolateContext(context.Background(), isolate) + isolate.ref() runtime.SetFinalizer(isolate, (*Isolate).release) tracer.Add(isolate) + go isolate.processCallbacks() + return isolate } @@ -71,15 +168,25 @@ func NewIsolateWithSnapshot(snapshot *Snapshot) *Isolate { running: true, data: map[string]interface{}{}, shutdownHooks: []interface{}{}, + callbacks: make(chan callbackInfo), + close: make(chan bool), } + isolate.executionContext = withIsolateContext(context.Background(), isolate) + isolate.ref() runtime.SetFinalizer(isolate, (*Isolate).release) tracer.Add(isolate) + go isolate.processCallbacks() + return isolate } +func (i *Isolate) GetExecutionContext() *ExecutionContext { + return FromContext(i.executionContext) +} + func (i *Isolate) ref() refutils.ID { return isolateRefs.Ref(i) } @@ -88,24 +195,130 @@ func (i *Isolate) unref() { isolateRefs.Unref(i) } -func (i *Isolate) lock() error { - i.mutex.RefLock() - if !i.running { - defer i.mutex.RefUnlock() - return fmt.Errorf("isolate terminated") +func (i *Isolate) processCallbacks() { + i.AddShutdownHook(func(i *Isolate) { + i.close <- true + }) + + for true { + select { + case callback := <-i.callbacks: + value, err := callback.fn(i.executionContext) + callback.result <- callbackResult{value, err} + case <-i.close: + return + } + } +} + +func (i *Isolate) Sync(ctx context.Context, fn func(context.Context) (*Value, error)) (*Value, error) { + executionContext := FromContext(ctx) + + if locked := executionContext.entrantMutex.TryLock(); locked { + executionContext.enter() + defer executionContext.exit() + defer executionContext.entrantMutex.Unlock() } - return nil + + if executionContext.isolate == i { + return fn(i.executionContext) + } + + ch := make(chan callbackResult) + + i.callbacks <- callbackInfo{fn, ch} + + result := <-ch + + return result.value, result.err +} + +func (i *Isolate) lock(ctx context.Context) (bool, error) { + context := FromContext(ctx) + // fmt.Println("locked", context.locked) + + if context == nil { + panic("no context found") + return false, fmt.Errorf("context not found") + } + + context.mutex.Lock() + defer context.mutex.Unlock() + + if !context.locked { + stack := debug.Stack() + + timer := time.AfterFunc(1*time.Second, func() { + fmt.Println("\n\n\n\n----- LOCKED STACK TRACE\n\n\n") + if i.lockerStackTrace != nil { + fmt.Println(string(i.lockerStackTrace)) + } else { + fmt.Println("locked but no stack trace available") + } + + fmt.Println("\n\n\n\n----- LOCKING STACK TRACE\n\n\n") + fmt.Println(string(stack)) + // os.Exit(1) + }) + context.locked = true + i.mutex.Lock() + + timer.Stop() + + i.lockerStackTrace = stack + if !i.running { + context.locked = false + defer i.mutex.Unlock() + return false, fmt.Errorf("isolate terminated") + } + } else { + if !i.running { + return false, fmt.Errorf("isolate terminated") + } else { + return false, nil + } + } + + return true, nil } -func (i *Isolate) unlock() { - i.mutex.RefUnlock() +func (i *Isolate) unlock(ctx context.Context) { + context := FromContext(ctx) + if context == nil { + panic("no context found") + } + + context.mutex.Lock() + defer context.mutex.Unlock() + + if !context.locked { + panic("not locked in this context") + } + + i.mutex.Unlock() + i.lockerStackTrace = nil + context.locked = false + } -func (i *Isolate) IsRunning() bool { - i.mutex.RefLock() - defer i.mutex.RefUnlock() +func (i *Isolate) IsRunning(ctx context.Context) (bool, error) { + if locked, err := i.lock(ctx); err != nil { + return false, err + } else if locked { + defer i.unlock(ctx) + } - return i.running + return i.running, nil +} + +func (i *Isolate) IsActive() bool { + if locked := i.mutex.TryLock(); locked { + defer i.mutex.Unlock() + + return true + } + + return false } func (i *Isolate) AddShutdownHook(shutdownHook interface{}) { @@ -120,60 +333,77 @@ func (i *Isolate) SetData(key string, value interface{}) { i.data[key] = value } -func (i *Isolate) RequestGarbageCollectionForTesting() { - if err := i.lock(); err != nil { +func (i *Isolate) RequestGarbageCollectionForTesting(ctx context.Context) { + if locked, err := i.lock(ctx); err != nil { return - } else { - defer i.unlock() + } else if locked { + defer i.unlock(ctx) } C.v8_Isolate_RequestGarbageCollectionForTesting(i.pointer) } -func (i *Isolate) Enter() { - if err := i.lock(); err != nil { +func (i *Isolate) Enter(ctx context.Context) { + if locked, err := i.lock(ctx); err != nil { return - } else { - defer i.unlock() + } else if locked { + defer i.unlock(ctx) } C.v8_Isolate_Enter(i.pointer) } -func (i *Isolate) Exit() { - if err := i.lock(); err != nil { +func (i *Isolate) Exit(ctx context.Context) { + if locked, err := i.lock(ctx); err != nil { return - } else { - defer i.unlock() + } else if locked { + defer i.unlock(ctx) } C.v8_Isolate_Exit(i.pointer) } -func (i *Isolate) RunMicrotasks() { - if err := i.lock(); err != nil { - return - } else { - defer i.unlock() - } +func (i *Isolate) RunMicrotasksSync(ctx context.Context) error { + _, err := i.Sync(ctx, func(ctx context.Context) (*Value, error) { + if locked, err := i.lock(ctx); err != nil { + return nil, err + } else if locked { + defer i.unlock(ctx) + } - C.v8_Isolate_RunMicrotasks(i.pointer) + C.v8_Isolate_RunMicrotasks(i.pointer) + return nil, nil + }) + return err } -func (i *Isolate) EnqueueMicrotask(fn *Value) { - if err := i.lock(); err != nil { - return - } else { - defer i.unlock() - } +func (i *Isolate) RunMicrotasksInBackground() { + go func() { + ctx := WithContext(context.Background()) + if err := i.RunMicrotasksSync(ctx); err != nil { + fmt.Println(err) + } + }() +} - fn.context.ref() - defer fn.context.unref() +func (i *Isolate) EnqueueMicrotaskWithValue(ctx context.Context, fn *Value) error { + _, err := i.Sync(ctx, func(ctx context.Context) (*Value, error) { + if locked, err := i.lock(ctx); err != nil { + return nil, err + } else if locked { + defer i.unlock(ctx) + } + + fn.context.ref() + defer fn.context.unref() - fn.Ref() - defer fn.Unref() + fn.Ref() + defer fn.Unref() - C.v8_Isolate_EnqueueMicrotask(i.pointer, fn.context.pointer, fn.pointer) + C.v8_Isolate_EnqueueMicrotask(i.pointer, fn.context.pointer, fn.pointer) + return nil, nil + }) + return err } func (i *Isolate) Terminate() { @@ -217,21 +447,21 @@ func (i *Isolate) Terminate() { i.data = nil } -func (i *Isolate) SendLowMemoryNotification() { - if err := i.lock(); err != nil { +func (i *Isolate) SendLowMemoryNotification(ctx context.Context) { + if locked, err := i.lock(ctx); err != nil { return - } else { - defer i.unlock() + } else if locked { + defer i.unlock(ctx) } C.v8_Isolate_LowMemoryNotification(i.pointer) } -func (i *Isolate) GetHeapStatistics() (HeapStatistics, error) { - if err := i.lock(); err != nil { +func (i *Isolate) GetHeapStatistics(ctx context.Context) (HeapStatistics, error) { + if locked, err := i.lock(ctx); err != nil { return HeapStatistics{}, err - } else { - defer i.unlock() + } else if locked { + defer i.unlock(ctx) } hs := C.v8_Isolate_GetHeapStatistics(i.pointer) diff --git a/v8_isolate_test.go b/v8_isolate_test.go index 14e7743..00b30c8 100644 --- a/v8_isolate_test.go +++ b/v8_isolate_test.go @@ -3,6 +3,7 @@ package isolates import ( "bufio" "bytes" + "context" "os" "runtime" "testing" @@ -16,20 +17,22 @@ func TestMain(m *testing.M) { } func DumpTracerForBenchmark(b *testing.B) { + ctx := WithContext(context.Background()) var buf bytes.Buffer w := bufio.NewWriter(&buf) - DumpTracer(w, false) + DumpTracer(ctx, w, false) w.Flush() b.Logf("\n%s", string(buf.Bytes())) } func TestIsolateCreate(t *testing.T) { + ctx := WithContext(context.Background()) i := NewIsolate() - if c, err := i.NewContext(); err != nil { + if c, err := i.NewContext(ctx); err != nil { t.Error(err) - } else if value, err := c.Create(20); err != nil { + } else if value, err := c.Create(ctx, 20); err != nil { t.Error(err) - } else if fn, err := c.Run(` + } else if fn, err := c.Run(ctx, ` (() => { const fib = (n) => { if (n < 2) { @@ -41,9 +44,9 @@ func TestIsolateCreate(t *testing.T) { })() `, "index.js"); err != nil { t.Error(err) - } else if result, err := fn.Call(nil, value); err != nil { + } else if result, err := fn.Call(ctx, nil, value); err != nil { t.Error(err) - } else if n, err := result.Int64(); err != nil { + } else if n, err := result.Int64(ctx); err != nil { t.Error(err) } else if n != 6765 { t.Errorf("invalid result: %s", result) @@ -60,6 +63,8 @@ func BenchmarkIsolateCreate(b *testing.B) { i := NewIsolate() go func(i *Isolate) { + ctx := WithContext(context.Background()) + done := false go func() { @@ -70,11 +75,11 @@ func BenchmarkIsolateCreate(b *testing.B) { } }() - if c, err := i.NewContext(); err != nil { + if c, err := i.NewContext(ctx); err != nil { b.Error(err) - } else if value, err := c.Create(20); err != nil { + } else if value, err := c.Create(ctx, 20); err != nil { b.Error(err) - } else if fn, err := c.Run(` + } else if fn, err := c.Run(ctx, ` (() => { const fib = (n) => { if (n < 2) { @@ -86,9 +91,9 @@ func BenchmarkIsolateCreate(b *testing.B) { })() `, "index.js"); err != nil { b.Error(err) - } else if result, err := fn.Call(nil, value); err != nil { + } else if result, err := fn.Call(ctx, nil, value); err != nil { b.Error(err) - } else if n, err := result.Int64(); err != nil { + } else if n, err := result.Int64(ctx); err != nil { b.Error(err) } else if n != 6765 { b.Errorf("invalid result: %s", result) diff --git a/v8_promise.go b/v8_promise.go index 8fb57c4..f385336 100644 --- a/v8_promise.go +++ b/v8_promise.go @@ -6,7 +6,9 @@ package isolates import "C" import ( + "context" "fmt" + "reflect" "runtime" refutils "github.com/grexie/refutils" @@ -19,11 +21,11 @@ type Resolver struct { pointer C.ResolverPtr } -func (c *Context) NewResolver() (*Resolver, error) { - if err := c.isolate.lock(); err != nil { +func (c *Context) NewResolver(ctx context.Context) (*Resolver, error) { + if locked, err := c.isolate.lock(ctx); err != nil { return nil, err - } else { - defer c.isolate.unlock() + } else if locked { + defer c.isolate.unlock(ctx) } pr := C.v8_Promise_NewResolver(c.pointer) @@ -39,63 +41,76 @@ func (c *Context) NewResolver() (*Resolver, error) { return r, nil } -func (r *Resolver) ResolveWithValue(v *Value) error { - if err := r.context.isolate.lock(); err != nil { - return err - } else { - defer r.context.isolate.unlock() - } - - err := C.v8_Resolver_Resolve(r.context.pointer, r.pointer, v.pointer) - return r.context.isolate.newError(err) -} - -func (r *Resolver) Resolve(value interface{}) error { - if err := r.context.isolate.lock(); err != nil { - return err - } else { - defer r.context.isolate.unlock() - } +func (r *Resolver) ResolveWithValue(ctx context.Context, v *Value) error { + _, err := r.context.isolate.Sync(ctx, func(ctx context.Context) (*Value, error) { + if locked, err := r.context.isolate.lock(ctx); err != nil { + return nil, err + } else if locked { + defer r.context.isolate.unlock(ctx) + } - if v, err := r.context.Create(value); err != nil { - return err - } else { err := C.v8_Resolver_Resolve(r.context.pointer, r.pointer, v.pointer) - return r.context.isolate.newError(err) - } + return nil, r.context.isolate.newError(err) + }) + return err } -func (r *Resolver) RejectWithValue(v *Value) error { - if err := r.context.isolate.lock(); err != nil { - return err - } else { - defer r.context.isolate.unlock() - } - - err := C.v8_Resolver_Reject(r.context.pointer, r.pointer, v.pointer) - return r.context.isolate.newError(err) +func (r *Resolver) Resolve(ctx context.Context, value interface{}) error { + _, err := r.context.isolate.Sync(ctx, func(ctx context.Context) (*Value, error) { + if v, err := r.context.create(ctx, reflect.ValueOf(value)); err != nil { + return nil, err + } else { + if locked, err := r.context.isolate.lock(ctx); err != nil { + return nil, err + } else if locked { + defer r.context.isolate.unlock(ctx) + } + + err := C.v8_Resolver_Resolve(r.context.pointer, r.pointer, v.pointer) + return nil, r.context.isolate.newError(err) + } + }) + return err } -func (r *Resolver) Reject(value interface{}) error { - if err := r.context.isolate.lock(); err != nil { - return err - } else { - defer r.context.isolate.unlock() - } +func (r *Resolver) RejectWithValue(ctx context.Context, v *Value) error { + _, err := r.context.isolate.Sync(ctx, func(ctx context.Context) (*Value, error) { + if locked, err := r.context.isolate.lock(ctx); err != nil { + return nil, err + } else if locked { + defer r.context.isolate.unlock(ctx) + } - if v, err := r.context.Create(value); err != nil { - return err - } else { err := C.v8_Resolver_Reject(r.context.pointer, r.pointer, v.pointer) - return r.context.isolate.newError(err) - } + return nil, r.context.isolate.newError(err) + }) + return err +} + +func (r *Resolver) Reject(ctx context.Context, value interface{}) error { + _, err := r.context.isolate.Sync(ctx, func(ctx context.Context) (*Value, error) { + if v, err := r.context.create(ctx, reflect.ValueOf(value)); err != nil { + return nil, err + } else { + if locked, err := r.context.isolate.lock(ctx); err != nil { + return nil, err + } else if locked { + defer r.context.isolate.unlock(ctx) + } + + err := C.v8_Resolver_Reject(r.context.pointer, r.pointer, v.pointer) + return nil, r.context.isolate.newError(err) + } + }) + + return err } -func (r *Resolver) Promise() (*Value, error) { - if err := r.context.isolate.lock(); err != nil { +func (r *Resolver) Promise(ctx context.Context) (*Value, error) { + if locked, err := r.context.isolate.lock(ctx); err != nil { return nil, err - } else { - defer r.context.isolate.unlock() + } else if locked { + defer r.context.isolate.unlock(ctx) } pv := C.v8_Resolver_GetPromise(r.context.pointer, r.pointer) @@ -104,18 +119,23 @@ func (r *Resolver) Promise() (*Value, error) { } func (r *Resolver) release() { - tracer.Remove(r) - runtime.SetFinalizer(r, nil) - - if err := r.context.isolate.lock(); err != nil { - return - } else { - defer r.context.isolate.unlock() - } - - if r.context.pointer != nil { - C.v8_Resolver_Release(r.context.pointer, r.pointer) - } - r.context = nil - r.pointer = nil + ctx := WithContext(context.Background()) + r.context.isolate.Sync(ctx, func(ctx context.Context) (*Value, error) { + tracer.Remove(r) + runtime.SetFinalizer(r, nil) + + if locked, err := r.context.isolate.lock(ctx); err != nil { + return nil, nil + } else if locked { + defer r.context.isolate.unlock(ctx) + } + + if r.context.pointer != nil { + C.v8_Resolver_Release(r.context.pointer, r.pointer) + } + r.context = nil + r.pointer = nil + + return nil, nil + }) } diff --git a/v8_tracer.go b/v8_tracer.go index 42dc894..dd50804 100644 --- a/v8_tracer.go +++ b/v8_tracer.go @@ -1,6 +1,7 @@ package isolates import ( + "context" "fmt" "io" "log" @@ -24,7 +25,7 @@ type itracer interface { Remove(value refutils.Ref) AddRefMap(name string, referenceMap *refutils.RefMap) RemoveRefMap(name string, referenceMap *refutils.RefMap) - Dump(w io.Writer, allocations bool) + Dump(ctx context.Context, w io.Writer, allocations bool) } type TracerType uint8 @@ -59,21 +60,21 @@ func StopTracer(t TracerType) { tracer = &nullTracer{} } -func DumpTracer(w io.Writer, allocations bool) { - tracer.Dump(w, allocations) +func DumpTracer(ctx context.Context, w io.Writer, allocations bool) { + tracer.Dump(ctx, w, allocations) } type nullTracer struct{} -func (t *nullTracer) Start() {} -func (t *nullTracer) Stop() {} -func (t *nullTracer) EnableAllocationStackTraces() {} -func (t *nullTracer) DisableAllocationStackTraces() {} -func (t *nullTracer) Add(value refutils.Ref) {} -func (t *nullTracer) Remove(value refutils.Ref) {} -func (t *nullTracer) AddRefMap(name string, refMap *refutils.RefMap) {} -func (t *nullTracer) RemoveRefMap(name string, refMap *refutils.RefMap) {} -func (t *nullTracer) Dump(w io.Writer, allocations bool) {} +func (t *nullTracer) Start() {} +func (t *nullTracer) Stop() {} +func (t *nullTracer) EnableAllocationStackTraces() {} +func (t *nullTracer) DisableAllocationStackTraces() {} +func (t *nullTracer) Add(value refutils.Ref) {} +func (t *nullTracer) Remove(value refutils.Ref) {} +func (t *nullTracer) AddRefMap(name string, refMap *refutils.RefMap) {} +func (t *nullTracer) RemoveRefMap(name string, refMap *refutils.RefMap) {} +func (t *nullTracer) Dump(ctx context.Context, w io.Writer, allocations bool) {} type simpleTracerAddMessage struct { Ref refutils.Ref @@ -264,7 +265,7 @@ func sortedMapStringUint64(m map[string]uint64, f func(k string, v uint64)) { } } -func (t *simpleTracer) Dump(w io.Writer, allocations bool) { +func (t *simpleTracer) Dump(ctx context.Context, w io.Writer, allocations bool) { t.mutex.Lock() defer t.mutex.Unlock() @@ -305,7 +306,7 @@ func (t *simpleTracer) Dump(w io.Writer, allocations bool) { stats["peak malloced memory"] = 0 for _, isolate := range isolateRefs.Refs() { - if hs, err := isolate.(*Isolate).GetHeapStatistics(); err != nil { + if hs, err := isolate.(*Isolate).GetHeapStatistics(ctx); err != nil { continue } else { stats["total heap size"] += hs.TotalHeapSize @@ -352,6 +353,8 @@ func (t *simpleTracer) Dump(w io.Writer, allocations bool) { func TracerHandler() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := WithContext(context.Background()) + w.Write([]byte(` @@ -361,7 +364,7 @@ func TracerHandler() http.Handler {
 `))
-		tracer.Dump(w, false)
+		tracer.Dump(ctx, w, false)
 		w.Write([]byte(`
 
diff --git a/v8_unmarshal.go b/v8_unmarshal.go index 04b0f85..4b94ce3 100644 --- a/v8_unmarshal.go +++ b/v8_unmarshal.go @@ -6,11 +6,12 @@ package isolates import "C" import ( + "context" "fmt" "reflect" ) -func (v *Value) Unmarshal(t reflect.Type) (*reflect.Value, error) { +func (v *Value) Unmarshal(ctx context.Context, t reflect.Type) (*reflect.Value, error) { if t == valueType { v := reflect.ValueOf(v) return &v, nil @@ -18,21 +19,21 @@ func (v *Value) Unmarshal(t reflect.Type) (*reflect.Value, error) { switch t.Kind() { case reflect.Bool: - if value, err := v.Bool(); err != nil { + if value, err := v.Bool(ctx); err != nil { return nil, err } else { v := reflect.ValueOf(value).Convert(t) return &v, nil } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - if value, err := v.Int64(); err != nil { + if value, err := v.Int64(ctx); err != nil { return nil, err } else { v := reflect.ValueOf(value).Convert(t) return &v, nil } case reflect.Float32, reflect.Float64: - if value, err := v.Float64(); err != nil { + if value, err := v.Float64(ctx); err != nil { return nil, err } else { v := reflect.ValueOf(value).Convert(t) @@ -43,8 +44,12 @@ func (v *Value) Unmarshal(t reflect.Type) (*reflect.Value, error) { case reflect.Ptr, reflect.Interface: case reflect.Map: case reflect.String: - v := reflect.ValueOf(v.String()).Convert(t) - return &v, nil + if string, err := v.String(ctx); err != nil { + return nil, err + } else { + v := reflect.ValueOf(string).Convert(t) + return &v, nil + } case reflect.Struct: } diff --git a/v8_value.go b/v8_value.go index 40636c1..398802d 100644 --- a/v8_value.go +++ b/v8_value.go @@ -6,6 +6,7 @@ package isolates import "C" import ( + "context" "errors" "reflect" "runtime" @@ -31,14 +32,16 @@ type PropertyDescriptor struct { Configurable bool } -func (c *Context) newValueFromTuple(vt C.ValueTuple) (*Value, error) { - if err := c.isolate.lock(); err != nil { - return nil, err - } else { - defer c.isolate.unlock() - } +func (c *Context) newValueFromTuple(ctx context.Context, vt C.ValueTuple) (*Value, error) { + return c.isolate.Sync(ctx, func(ctx context.Context) (*Value, error) { + if locked, err := c.isolate.lock(ctx); err != nil { + return nil, err + } else if locked { + defer c.isolate.unlock(ctx) + } - return c.newValue(vt.value, vt.kinds), c.isolate.newError(vt.error) + return c.newValue(vt.value, vt.kinds), c.isolate.newError(vt.error) + }) } func (c *Context) newValue(pointer C.ValuePtr, k C.Kinds) *Value { @@ -75,11 +78,11 @@ func (v *Value) GetContext() *Context { return v.context } -func (v *Value) DefineProperty(key string, descriptor *PropertyDescriptor) error { - if err := v.context.isolate.lock(); err != nil { +func (v *Value) DefineProperty(ctx context.Context, key string, descriptor *PropertyDescriptor) error { + if locked, err := v.context.isolate.lock(ctx); err != nil { return err - } else { - defer v.context.isolate.unlock() + } else if locked { + defer v.context.isolate.unlock(ctx) } pk := C.CString(key) @@ -88,34 +91,34 @@ func (v *Value) DefineProperty(key string, descriptor *PropertyDescriptor) error return v.context.isolate.newError(err) } -func (v *Value) Get(key string) (*Value, error) { - if err := v.context.isolate.lock(); err != nil { +func (v *Value) Get(ctx context.Context, key string) (*Value, error) { + if locked, err := v.context.isolate.lock(ctx); err != nil { return nil, err - } else { - defer v.context.isolate.unlock() + } else if locked { + defer v.context.isolate.unlock(ctx) } pk := C.CString(key) vt := C.v8_Value_Get(v.context.pointer, v.pointer, pk) C.free(unsafe.Pointer(pk)) - return v.context.newValueFromTuple(vt) + return v.context.newValueFromTuple(ctx, vt) } -func (v *Value) GetIndex(i int) (*Value, error) { - if err := v.context.isolate.lock(); err != nil { +func (v *Value) GetIndex(ctx context.Context, i int) (*Value, error) { + if locked, err := v.context.isolate.lock(ctx); err != nil { return nil, err - } else { - defer v.context.isolate.unlock() + } else if locked { + defer v.context.isolate.unlock(ctx) } - return v.context.newValueFromTuple(C.v8_Value_GetIndex(v.context.pointer, v.pointer, C.int(i))) + return v.context.newValueFromTuple(ctx, C.v8_Value_GetIndex(v.context.pointer, v.pointer, C.int(i))) } -func (v *Value) Set(key string, value *Value) error { - if err := v.context.isolate.lock(); err != nil { +func (v *Value) Set(ctx context.Context, key string, value *Value) error { + if locked, err := v.context.isolate.lock(ctx); err != nil { return err - } else { - defer v.context.isolate.unlock() + } else if locked { + defer v.context.isolate.unlock(ctx) } pk := C.CString(key) @@ -124,21 +127,21 @@ func (v *Value) Set(key string, value *Value) error { return v.context.isolate.newError(err) } -func (v *Value) SetIndex(i int, value *Value) error { - if err := v.context.isolate.lock(); err != nil { +func (v *Value) SetIndex(ctx context.Context, i int, value *Value) error { + if locked, err := v.context.isolate.lock(ctx); err != nil { return err - } else { - defer v.context.isolate.unlock() + } else if locked { + defer v.context.isolate.unlock(ctx) } return v.context.isolate.newError(C.v8_Value_SetIndex(v.context.pointer, v.pointer, C.int(i), value.pointer)) } -func (v *Value) SetInternalField(i int, value uint32) error { - if err := v.context.isolate.lock(); err != nil { +func (v *Value) SetInternalField(ctx context.Context, i int, value uint32) error { + if locked, err := v.context.isolate.lock(ctx); err != nil { return err - } else { - defer v.context.isolate.unlock() + } else if locked { + defer v.context.isolate.unlock(ctx) } v.context.ref() @@ -148,11 +151,11 @@ func (v *Value) SetInternalField(i int, value uint32) error { return nil } -func (v *Value) GetInternalField(i int) (int64, error) { - if err := v.context.isolate.lock(); err != nil { +func (v *Value) GetInternalField(ctx context.Context, i int) (int64, error) { + if locked, err := v.context.isolate.lock(ctx); err != nil { return 0, err - } else { - defer v.context.isolate.unlock() + } else if locked { + defer v.context.isolate.unlock(ctx) } v.context.ref() @@ -161,11 +164,11 @@ func (v *Value) GetInternalField(i int) (int64, error) { return int64(C.v8_Object_GetInternalField(v.context.pointer, v.pointer, C.int(i))), nil } -func (v *Value) GetInternalFieldCount() (int, error) { - if err := v.context.isolate.lock(); err != nil { +func (v *Value) GetInternalFieldCount(ctx context.Context) (int, error) { + if locked, err := v.context.isolate.lock(ctx); err != nil { return 0, err - } else { - defer v.context.isolate.unlock() + } else if locked { + defer v.context.isolate.unlock(ctx) } v.context.ref() @@ -173,67 +176,72 @@ func (v *Value) GetInternalFieldCount() (int, error) { return int(C.v8_Object_GetInternalFieldCount(v.context.pointer, v.pointer)), nil } -func (v *Value) Bind(argv ...*Value) (*Value, error) { - if err := v.context.isolate.lock(); err != nil { +func (v *Value) Bind(ctx context.Context, argv ...*Value) (*Value, error) { + if locked, err := v.context.isolate.lock(ctx); err != nil { return nil, err - } else { - defer v.context.isolate.unlock() + } else if locked { + defer v.context.isolate.unlock(ctx) } - if bind, err := v.Get("bind"); err != nil { + if bind, err := v.Get(ctx, "bind"); err != nil { return nil, err - } else if fn, err := bind.Call(v, argv...); err != nil { + } else if fn, err := bind.Call(ctx, v, argv...); err != nil { return nil, err } else { return fn, nil } } -func (v *Value) Call(self *Value, argv ...*Value) (*Value, error) { - if err := v.context.isolate.lock(); err != nil { - return nil, err - } else { - defer v.context.isolate.unlock() - } +func (v *Value) Call(ctx context.Context, self *Value, argv ...*Value) (*Value, error) { + return v.context.isolate.Sync(ctx, func(ctx context.Context) (*Value, error) { + if locked, err := v.context.isolate.lock(ctx); err != nil { + return nil, err + } else if locked { + defer v.context.isolate.unlock(ctx) + } - pargv := make([]C.ValuePtr, len(argv)+1) - for i, argvi := range argv { - pargv[i] = argvi.pointer - } + pargv := make([]C.ValuePtr, len(argv)+1) + for i, argvi := range argv { + pargv[i] = argvi.pointer + } - pself := C.ValuePtr(nil) - if self != nil { - pself = self.pointer - } + pself := C.ValuePtr(nil) + if self != nil { + pself = self.pointer + } - v.context.ref() - defer v.context.unref() + v.context.ref() + defer v.context.unref() - vt := C.v8_Value_Call(v.context.pointer, v.pointer, pself, C.int(len(argv)), &pargv[0]) - return v.context.newValueFromTuple(vt) + vt := C.v8_Value_Call(v.context.pointer, v.pointer, pself, C.int(len(argv)), &pargv[0]) + return v.context.newValueFromTuple(ctx, vt) + }) } -func (v *Value) CallMethod(name string, argv ...*Value) (*Value, error) { - if err := v.context.isolate.lock(); err != nil { - return nil, err - } else { - defer v.context.isolate.unlock() - } +func (v *Value) CallMethod(ctx context.Context, name string, argv ...*Value) (*Value, error) { + return v.context.isolate.Sync(ctx, func(ctx context.Context) (*Value, error) { + if locked, err := v.context.isolate.lock(ctx); err != nil { + return nil, err + } else if locked { + defer v.context.isolate.unlock(ctx) + } + + if m, err := v.Get(ctx, name); err != nil { + return nil, err + } else if value, err := m.Call(ctx, v, argv...); err != nil { + return nil, err + } else { + return value, nil + } + }) - if m, err := v.Get(name); err != nil { - return nil, err - } else if value, err := m.Call(v, argv...); err != nil { - return nil, err - } else { - return value, nil - } } -func (v *Value) New(argv ...*Value) (*Value, error) { - if err := v.context.isolate.lock(); err != nil { +func (v *Value) New(ctx context.Context, argv ...*Value) (*Value, error) { + if locked, err := v.context.isolate.lock(ctx); err != nil { return nil, err - } else { - defer v.context.isolate.unlock() + } else if locked { + defer v.context.isolate.unlock(ctx) } pargv := make([]C.ValuePtr, len(argv)+1) @@ -243,14 +251,15 @@ func (v *Value) New(argv ...*Value) (*Value, error) { v.context.ref() vt := C.v8_Value_New(v.context.pointer, v.pointer, C.int(len(argv)), &pargv[0]) v.context.unref() - return v.context.newValueFromTuple(vt) + return v.context.newValueFromTuple(ctx, vt) } -func (v *Value) Bytes() ([]byte, error) { - if err := v.context.isolate.lock(); err != nil { +func (v *Value) Bytes(ctx context.Context) ([]byte, error) { + + if locked, err := v.context.isolate.lock(ctx); err != nil { return nil, err - } else { - defer v.context.isolate.unlock() + } else if locked { + defer v.context.isolate.unlock(ctx) } b := C.v8_Value_Bytes(v.context.pointer, v.pointer) @@ -263,11 +272,11 @@ func (v *Value) Bytes() ([]byte, error) { return buf, nil } -func (v *Value) SetBytes(bytes []byte) error { - if err := v.context.isolate.lock(); err != nil { +func (v *Value) SetBytes(ctx context.Context, bytes []byte) error { + if locked, err := v.context.isolate.lock(ctx); err != nil { return err - } else { - defer v.context.isolate.unlock() + } else if locked { + defer v.context.isolate.unlock(ctx) } b := C.v8_Value_Bytes(v.context.pointer, v.pointer) @@ -279,11 +288,11 @@ func (v *Value) SetBytes(bytes []byte) error { return nil } -func (v *Value) GetByteLength() (int, error) { - if err := v.context.isolate.lock(); err != nil { +func (v *Value) GetByteLength(ctx context.Context) (int, error) { + if locked, err := v.context.isolate.lock(ctx); err != nil { return 0, err - } else { - defer v.context.isolate.unlock() + } else if locked { + defer v.context.isolate.unlock(ctx) } bytes := C.v8_Value_ByteLength(v.context.pointer, v.pointer) @@ -291,46 +300,45 @@ func (v *Value) GetByteLength() (int, error) { return int(bytes), nil } -func (v *Value) Int64() (int64, error) { - if err := v.context.isolate.lock(); err != nil { +func (v *Value) Int64(ctx context.Context) (int64, error) { + if locked, err := v.context.isolate.lock(ctx); err != nil { return 0, err - } else { - defer v.context.isolate.unlock() + } else if locked { + defer v.context.isolate.unlock(ctx) } return int64(C.v8_Value_Int64(v.context.pointer, v.pointer)), nil } -func (v *Value) Float64() (float64, error) { - if err := v.context.isolate.lock(); err != nil { +func (v *Value) Float64(ctx context.Context) (float64, error) { + if locked, err := v.context.isolate.lock(ctx); err != nil { return 0, err - } else { - defer v.context.isolate.unlock() + } else if locked { + defer v.context.isolate.unlock(ctx) } return float64(C.v8_Value_Float64(v.context.pointer, v.pointer)), nil } -func (v *Value) Bool() (bool, error) { - if err := v.context.isolate.lock(); err != nil { +func (v *Value) Bool(ctx context.Context) (bool, error) { + if locked, err := v.context.isolate.lock(ctx); err != nil { return false, err - } else { - defer v.context.isolate.unlock() + } else if locked { + defer v.context.isolate.unlock(ctx) } - return C.v8_Value_Bool(v.context.pointer, v.pointer) == 1, nil } -func (v *Value) Date() (time.Time, error) { - if err := v.context.isolate.lock(); err != nil { +func (v *Value) Date(ctx context.Context) (time.Time, error) { + if locked, err := v.context.isolate.lock(ctx); err != nil { return time.Time{}, err - } else { - defer v.context.isolate.unlock() + } else if locked { + defer v.context.isolate.unlock(ctx) } if !v.IsKind(KindDate) { return time.Time{}, errors.New("not a date") - } else if ms, err := v.Int64(); err != nil { + } else if ms, err := v.Int64(ctx); err != nil { return time.Time{}, nil } else { s := ms / 1000 @@ -339,93 +347,95 @@ func (v *Value) Date() (time.Time, error) { } } -func (v *Value) PromiseInfo() (PromiseState, *Value, error) { - if err := v.context.isolate.lock(); err != nil { +func (v *Value) PromiseInfo(ctx context.Context) (PromiseState, *Value, error) { + if locked, err := v.context.isolate.lock(ctx); err != nil { return 0, nil, err - } else { - defer v.context.isolate.unlock() + } else if locked { + defer v.context.isolate.unlock(ctx) } if !v.IsKind(KindPromise) { return 0, nil, errors.New("not a promise") } var state C.int - p, err := v.context.newValueFromTuple(C.v8_Value_PromiseInfo(v.context.pointer, v.pointer, &state)) + p, err := v.context.newValueFromTuple(ctx, C.v8_Value_PromiseInfo(v.context.pointer, v.pointer, &state)) return PromiseState(state), p, err } -func (v *Value) String() string { - if err := v.context.isolate.lock(); err != nil { - return err.Error() - } else { - defer v.context.isolate.unlock() +func (v *Value) String(ctx context.Context) (string, error) { + if locked, err := v.context.isolate.lock(ctx); err != nil { + return "", err + } else if locked { + defer v.context.isolate.unlock(ctx) } ps := C.v8_Value_String(v.context.pointer, v.pointer) defer C.free(unsafe.Pointer(ps.data)) s := C.GoStringN(ps.data, ps.length) - return s + return s, nil } -func (v *Value) MarshalJSON() ([]byte, error) { - if err := v.context.isolate.lock(); err != nil { +func (v *Value) MarshalJSON(ctx context.Context) ([]byte, error) { + if locked, err := v.context.isolate.lock(ctx); err != nil { return nil, err - } else { - defer v.context.isolate.unlock() + } else if locked { + defer v.context.isolate.unlock(ctx) } - if s, err := v.context.newValueFromTuple(C.v8_JSON_Stringify(v.context.pointer, v.pointer)); err != nil { + if s, err := v.context.newValueFromTuple(ctx, C.v8_JSON_Stringify(v.context.pointer, v.pointer)); err != nil { + return nil, err + } else if string, err := s.String(ctx); err != nil { return nil, err } else { - return []byte(s.String()), nil + return []byte(string), nil } } -func (v *Value) receiver() *valueRef { - if err := v.context.isolate.lock(); err != nil { - return nil - } else { - defer v.context.isolate.unlock() +func (v *Value) receiver(ctx context.Context) (*valueRef, error) { + if locked, err := v.context.isolate.lock(ctx); err != nil { + return nil, err + } else if locked { + defer v.context.isolate.unlock(ctx) } - if n, err := v.GetInternalFieldCount(); err != nil || n == 0 { - return nil + if n, err := v.GetInternalFieldCount(ctx); err != nil || n == 0 { + return nil, err } - intfield, err := v.GetInternalField(0) + intfield, err := v.GetInternalField(ctx, 0) if err != nil { - return nil + return nil, err } idn := refutils.ID(intfield) if idn == 0 { - return nil + return nil, err } ref := v.context.values.Get(idn) if ref == nil { - return nil + return nil, err } if vref, ok := ref.(*valueRef); !ok { - return nil + return nil, err } else { - return vref + return vref, nil } } -func (v *Value) Receiver(t reflect.Type) *reflect.Value { +func (v *Value) Receiver(ctx context.Context, t reflect.Type) (*reflect.Value, error) { var r reflect.Value - if vref := v.receiver(); vref == nil { - return nil + if vref, err := v.receiver(ctx); err != nil { + return nil, err } else { r = vref.value } if t.Kind() == reflect.Interface && r.Type().ConvertibleTo(t) { r = r.Convert(t) - return &r + return &r, nil } ptr := t.Kind() == reflect.Ptr @@ -439,7 +449,8 @@ func (v *Value) Receiver(t reflect.Type) *reflect.Value { } if rt != t { - return nil + // TODO: should this return an error? + return nil, nil } if ptr && r.Kind() != reflect.Ptr { @@ -448,30 +459,30 @@ func (v *Value) Receiver(t reflect.Type) *reflect.Value { r = r.Elem() } - return &r + return &r, nil } -func (v *Value) SetReceiver(value *reflect.Value) { +func (v *Value) SetReceiver(ctx context.Context, value *reflect.Value) error { if !v.IsKind(KindObject) { - return + return nil } - if n, err := v.GetInternalFieldCount(); err != nil || n == 0 { - return + if n, err := v.GetInternalFieldCount(ctx); err != nil || n == 0 { + return nil } - if vref := v.receiver(); vref != nil { + if vref, err := v.receiver(ctx); err != nil { v.context.values.Release(vref) } if value == nil { - v.SetInternalField(0, 0) - return + v.SetInternalField(ctx, 0, 0) + return nil } id := v.context.values.Ref(&valueRef{value: *value}) // v.setID("r", id) - v.SetInternalField(0, uint32(id)) + return v.SetInternalField(ctx, 0, uint32(id)) } // func (v *Value) AddFinalizer(finalizer func()) { @@ -530,39 +541,26 @@ func valueWeakCallbackHandler(pid C.String) { // return nil } -func (v *Value) release() { - // if false { - // iid := v.context.isolate.ref() - // defer v.context.isolate.unref() - // - // cid := v.context.ref() - // defer v.context.unref() - // - // vid := v.ref() - // defer v.unref() - // - // id := fmt.Sprintf("%d:%d:%d", iid, cid, vid) - // - // v.setWeak(id, func() { - // for _, finalizer := range v.finalizers { - // finalizer() - // } - // v.finalize() - // }) - // } else { - // v.finalize() - // } - tracer.Remove(v) - runtime.SetFinalizer(v, nil) +func (v *Value) releaseWithContext(ctx context.Context) { + v.context.isolate.Sync(ctx, func(ctx context.Context) (*Value, error) { + tracer.Remove(v) + runtime.SetFinalizer(v, nil) - if err := v.context.isolate.lock(); err == nil { - defer v.context.isolate.unlock() - } + if locked, _ := v.context.isolate.lock(ctx); locked { + defer v.context.isolate.unlock(ctx) + } - if v.context.pointer != nil { - C.v8_Value_Release(v.context.pointer, v.pointer) - } - v.context = nil - v.pointer = nil + if v.context.pointer != nil { + C.v8_Value_Release(v.context.pointer, v.pointer) + } + v.context = nil + v.pointer = nil + return nil, nil + }) +} + +func (v *Value) release() { + ctx := WithContext(context.Background()) + v.releaseWithContext(ctx) }