2019-06-24 00:30:12 +00:00
|
|
|
package scripting
|
|
|
|
|
|
|
|
import (
|
|
|
|
"git.kirsle.net/apps/doodle/pkg/log"
|
2022-01-17 04:09:27 +00:00
|
|
|
"github.com/dop251/goja"
|
2019-06-24 00:30:12 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Message holds data being published from one script VM with information sent
|
|
|
|
// to the linked VMs.
|
|
|
|
type Message struct {
|
2021-10-10 03:45:38 +00:00
|
|
|
Name string
|
|
|
|
SenderID string
|
2022-01-17 04:09:27 +00:00
|
|
|
Args []goja.Value
|
2019-06-24 00:30:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
RegisterPublishHooks adds the pub/sub hooks to a JavaScript VM.
|
|
|
|
|
|
|
|
This adds the global methods `Message.Subscribe(name, func)` and
|
|
|
|
`Message.Publish(name, args)` to the JavaScript VM's scope.
|
|
|
|
*/
|
2019-12-31 02:13:28 +00:00
|
|
|
func RegisterPublishHooks(s *Supervisor, vm *VM) {
|
2019-06-24 00:30:12 +00:00
|
|
|
// Goroutine to watch the VM's inbound channel and invoke Subscribe handlers
|
|
|
|
// for any matching messages received.
|
|
|
|
go func() {
|
2022-01-19 05:24:36 +00:00
|
|
|
// Catch any exceptions raised by the JavaScript VM.
|
|
|
|
defer func() {
|
|
|
|
if err := recover(); err != nil {
|
|
|
|
// TODO EXCEPTIONS
|
|
|
|
log.Error("RegisterPublishHooks(%s): %s", vm.Name, err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2022-04-10 19:39:27 +00:00
|
|
|
// Watch the Inbound channel for PubSub messages and the stop channel for Teardown.
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-vm.stop:
|
|
|
|
log.Info("JavaScript VM %s stopping PubSub goroutine", vm.Name)
|
|
|
|
return
|
|
|
|
case msg := <-vm.Inbound:
|
|
|
|
vm.muSubscribe.Lock()
|
|
|
|
|
|
|
|
if _, ok := vm.subscribe[msg.Name]; ok {
|
|
|
|
for _, callback := range vm.subscribe[msg.Name] {
|
|
|
|
log.Debug("PubSub: %s receives from %s: %s", vm.Name, msg.SenderID, msg.Name)
|
|
|
|
if function, ok := goja.AssertFunction(callback); ok {
|
|
|
|
function(goja.Undefined(), msg.Args...)
|
|
|
|
}
|
2022-01-17 04:09:27 +00:00
|
|
|
}
|
2019-06-24 00:30:12 +00:00
|
|
|
}
|
2021-10-10 03:45:38 +00:00
|
|
|
|
2022-04-10 19:39:27 +00:00
|
|
|
vm.muSubscribe.Unlock()
|
|
|
|
}
|
2019-06-24 00:30:12 +00:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// Register the Message.Subscribe and Message.Publish functions.
|
|
|
|
vm.vm.Set("Message", map[string]interface{}{
|
2022-01-17 04:09:27 +00:00
|
|
|
"Subscribe": func(name string, callback goja.Value) {
|
2020-01-03 01:58:22 +00:00
|
|
|
vm.muSubscribe.Lock()
|
|
|
|
defer vm.muSubscribe.Unlock()
|
|
|
|
|
2022-01-17 04:09:27 +00:00
|
|
|
if _, ok := goja.AssertFunction(callback); !ok {
|
2019-06-24 00:30:12 +00:00
|
|
|
log.Error("SUBSCRIBE(%s): callback is not a function", name)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if _, ok := vm.subscribe[name]; !ok {
|
2022-01-17 04:09:27 +00:00
|
|
|
vm.subscribe[name] = []goja.Value{}
|
2019-06-24 00:30:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
vm.subscribe[name] = append(vm.subscribe[name], callback)
|
|
|
|
},
|
|
|
|
|
2022-01-17 04:09:27 +00:00
|
|
|
"Publish": func(name string, v ...goja.Value) {
|
2019-06-24 00:30:12 +00:00
|
|
|
for _, channel := range vm.Outbound {
|
|
|
|
channel <- Message{
|
2021-10-10 03:45:38 +00:00
|
|
|
Name: name,
|
|
|
|
SenderID: vm.Name,
|
|
|
|
Args: v,
|
2019-06-24 00:30:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2019-12-31 02:13:28 +00:00
|
|
|
|
2022-01-17 04:09:27 +00:00
|
|
|
"Broadcast": func(name string, v ...goja.Value) {
|
2019-12-31 02:13:28 +00:00
|
|
|
// Send the message to all actor VMs.
|
2020-01-03 01:58:22 +00:00
|
|
|
for _, toVM := range s.scripts {
|
2022-01-19 05:24:36 +00:00
|
|
|
if toVM == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-01-03 01:58:22 +00:00
|
|
|
if vm.Name == toVM.Name {
|
|
|
|
log.Debug("Broadcast(%s): skip to vm '%s' cuz it is the sender", name, toVM.Name)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
toVM.Inbound <- Message{
|
2021-10-10 03:45:38 +00:00
|
|
|
Name: name,
|
|
|
|
SenderID: vm.Name,
|
|
|
|
Args: v,
|
2019-12-31 02:13:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2019-06-24 00:30:12 +00:00
|
|
|
})
|
|
|
|
}
|