mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			123 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			123 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Go
		
	
	
	
| // Copyright 2018 The Go Authors. All rights reserved.
 | |
| // Use of this source code is governed by a BSD-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| // +build js,wasm
 | |
| 
 | |
| package js
 | |
| 
 | |
| import "sync"
 | |
| 
 | |
| var (
 | |
| 	pendingCallbacks        = Global().Get("Array").New()
 | |
| 	makeCallbackHelper      = Global().Get("Go").Get("_makeCallbackHelper")
 | |
| 	makeEventCallbackHelper = Global().Get("Go").Get("_makeEventCallbackHelper")
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	callbacksMu    sync.Mutex
 | |
| 	callbacks             = make(map[uint32]func([]Value))
 | |
| 	nextCallbackID uint32 = 1
 | |
| )
 | |
| 
 | |
| // Callback is a Go function that got wrapped for use as a JavaScript callback.
 | |
| type Callback struct {
 | |
| 	Value // the JavaScript function that queues the callback for execution
 | |
| 	id    uint32
 | |
| }
 | |
| 
 | |
| // NewCallback returns a wrapped callback function.
 | |
| //
 | |
| // Invoking the callback in JavaScript will queue the Go function fn for execution.
 | |
| // This execution happens asynchronously on a special goroutine that handles all callbacks and preserves
 | |
| // the order in which the callbacks got called.
 | |
| // As a consequence, if one callback blocks this goroutine, other callbacks will not be processed.
 | |
| // A blocking callback should therefore explicitly start a new goroutine.
 | |
| //
 | |
| // Callback.Release must be called to free up resources when the callback will not be used any more.
 | |
| func NewCallback(fn func(args []Value)) Callback {
 | |
| 	callbackLoopOnce.Do(func() {
 | |
| 		go callbackLoop()
 | |
| 	})
 | |
| 
 | |
| 	callbacksMu.Lock()
 | |
| 	id := nextCallbackID
 | |
| 	nextCallbackID++
 | |
| 	callbacks[id] = fn
 | |
| 	callbacksMu.Unlock()
 | |
| 	return Callback{
 | |
| 		Value: makeCallbackHelper.Invoke(id, pendingCallbacks, jsGo),
 | |
| 		id:    id,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type EventCallbackFlag int
 | |
| 
 | |
| const (
 | |
| 	// PreventDefault can be used with NewEventCallback to call event.preventDefault synchronously.
 | |
| 	PreventDefault EventCallbackFlag = 1 << iota
 | |
| 	// StopPropagation can be used with NewEventCallback to call event.stopPropagation synchronously.
 | |
| 	StopPropagation
 | |
| 	// StopImmediatePropagation can be used with NewEventCallback to call event.stopImmediatePropagation synchronously.
 | |
| 	StopImmediatePropagation
 | |
| )
 | |
| 
 | |
| // NewEventCallback returns a wrapped callback function, just like NewCallback, but the callback expects to have
 | |
| // exactly one argument, the event. Depending on flags, it will synchronously call event.preventDefault,
 | |
| // event.stopPropagation and/or event.stopImmediatePropagation before queuing the Go function fn for execution.
 | |
| func NewEventCallback(flags EventCallbackFlag, fn func(event Value)) Callback {
 | |
| 	c := NewCallback(func(args []Value) {
 | |
| 		fn(args[0])
 | |
| 	})
 | |
| 	return Callback{
 | |
| 		Value: makeEventCallbackHelper.Invoke(
 | |
| 			flags&PreventDefault != 0,
 | |
| 			flags&StopPropagation != 0,
 | |
| 			flags&StopImmediatePropagation != 0,
 | |
| 			c,
 | |
| 		),
 | |
| 		id: c.id,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Release frees up resources allocated for the callback.
 | |
| // The callback must not be invoked after calling Release.
 | |
| func (c Callback) Release() {
 | |
| 	callbacksMu.Lock()
 | |
| 	delete(callbacks, c.id)
 | |
| 	callbacksMu.Unlock()
 | |
| }
 | |
| 
 | |
| var callbackLoopOnce sync.Once
 | |
| 
 | |
| func callbackLoop() {
 | |
| 	for !jsGo.Get("_callbackShutdown").Bool() {
 | |
| 		sleepUntilCallback()
 | |
| 		for {
 | |
| 			cb := pendingCallbacks.Call("shift")
 | |
| 			if cb == Undefined() {
 | |
| 				break
 | |
| 			}
 | |
| 
 | |
| 			id := uint32(cb.Get("id").Int())
 | |
| 			callbacksMu.Lock()
 | |
| 			f, ok := callbacks[id]
 | |
| 			callbacksMu.Unlock()
 | |
| 			if !ok {
 | |
| 				Global().Get("console").Call("error", "call to closed callback")
 | |
| 				continue
 | |
| 			}
 | |
| 
 | |
| 			argsObj := cb.Get("args")
 | |
| 			args := make([]Value, argsObj.Length())
 | |
| 			for i := range args {
 | |
| 				args[i] = argsObj.Index(i)
 | |
| 			}
 | |
| 			f(args)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // sleepUntilCallback is defined in the runtime package
 | |
| func sleepUntilCallback()
 |