This repository has been archived by the owner on Oct 19, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 92
/
worker.go
165 lines (135 loc) · 4.28 KB
/
worker.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package v8worker
/*
#cgo CXXFLAGS: -std=c++11
#cgo pkg-config: v8.pc
#include <stdlib.h>
#include "binding.h"
*/
import "C"
import "errors"
import "unsafe"
import "sync"
import "runtime"
type workerTableIndex int
var workerTableLock sync.Mutex
// This table will store all pointers to all active workers. Because we can't safely
// pass pointers to Go objects to C, we instead pass a key to this table.
var workerTable = make(map[workerTableIndex]*worker)
// Keeps track of the last used table index. Incremeneted when a worker is created.
var workerTableNextAvailable workerTableIndex = 0
// To receive messages from javascript...
type ReceiveMessageCallback func(msg string)
// To send a message from javascript and synchronously return a string.
type ReceiveSyncMessageCallback func(msg string) string
// Don't init V8 more than once.
var initV8Once sync.Once
// Internal worker struct which is stored in the workerTable.
// Weak-ref pattern https://groups.google.com/forum/#!topic/golang-nuts/1ItNOOj8yW8/discussion
type worker struct {
cWorker *C.worker
cb ReceiveMessageCallback
sync_cb ReceiveSyncMessageCallback
tableIndex workerTableIndex
}
// This is a golang wrapper around a single V8 Isolate.
type Worker struct {
*worker
disposed bool
}
// Return the V8 version E.G. "4.3.59"
func Version() string {
return C.GoString(C.worker_version())
}
func workerTableLookup(index workerTableIndex) *worker {
workerTableLock.Lock()
defer workerTableLock.Unlock()
return workerTable[index]
}
//export recvCb
func recvCb(msg_s *C.char, index workerTableIndex) {
msg := C.GoString(msg_s)
w := workerTableLookup(index)
w.cb(msg)
}
//export recvSyncCb
func recvSyncCb(msg_s *C.char, index workerTableIndex) *C.char {
msg := C.GoString(msg_s)
w := workerTableLookup(index)
return_s := C.CString(w.sync_cb(msg))
return return_s
}
// Creates a new worker, which corresponds to a V8 isolate. A single threaded
// standalone execution context.
func New(cb ReceiveMessageCallback, sync_cb ReceiveSyncMessageCallback) *Worker {
workerTableLock.Lock()
w := &worker{
cb: cb,
sync_cb: sync_cb,
tableIndex: workerTableNextAvailable,
}
workerTableNextAvailable++
workerTable[w.tableIndex] = w
workerTableLock.Unlock()
initV8Once.Do(func() {
C.v8_init()
})
w.cWorker = C.worker_new(C.int(w.tableIndex))
externalWorker := &Worker{
worker: w,
disposed: false,
}
runtime.SetFinalizer(externalWorker, func(final_worker *Worker) {
final_worker.Dispose()
})
return externalWorker
}
// Forcefully frees up memory associated with worker.
// GC will also free up worker memory so calling this isn't strictly necessary.
func (w *Worker) Dispose() {
if w.disposed {
panic("worker already disposed")
}
w.disposed = true
workerTableLock.Lock()
internalWorker := w.worker
delete(workerTable, internalWorker.tableIndex)
workerTableLock.Unlock()
C.worker_dispose(internalWorker.cWorker)
}
// Load and executes a javascript file with the filename specified by
// scriptName and the contents of the file specified by the param code.
func (w *Worker) Load(scriptName string, code string) error {
scriptName_s := C.CString(scriptName)
code_s := C.CString(code)
defer C.free(unsafe.Pointer(scriptName_s))
defer C.free(unsafe.Pointer(code_s))
r := C.worker_load(w.worker.cWorker, scriptName_s, code_s)
if r != 0 {
errStr := C.GoString(C.worker_last_exception(w.worker.cWorker))
return errors.New(errStr)
}
return nil
}
// Sends a message to a worker. The $recv callback in js will be called.
func (w *Worker) Send(msg string) error {
msg_s := C.CString(string(msg))
defer C.free(unsafe.Pointer(msg_s))
r := C.worker_send(w.worker.cWorker, msg_s)
if r != 0 {
errStr := C.GoString(C.worker_last_exception(w.worker.cWorker))
return errors.New(errStr)
}
return nil
}
// SendSync sends a message to a worker. The $recvSync callback in js will be called.
// That callback will return a string which is passed to golang and used as the return value of SendSync.
func (w *Worker) SendSync(msg string) string {
msg_s := C.CString(string(msg))
defer C.free(unsafe.Pointer(msg_s))
svalue := C.worker_send_sync(w.worker.cWorker, msg_s)
return C.GoString(svalue)
}
// Terminates execution of javascript
func (w *Worker) TerminateExecution() {
C.worker_terminate_execution(w.worker.cWorker)
}