...

Source file src/runtime/lock_js.go

     1	// Copyright 2018 The Go Authors. All rights reserved.
     2	// Use of this source code is governed by a BSD-style
     3	// license that can be found in the LICENSE file.
     4	
     5	// +build js,wasm
     6	
     7	package runtime
     8	
     9	import (
    10		_ "unsafe"
    11	)
    12	
    13	// js/wasm has no support for threads yet. There is no preemption.
    14	
    15	const (
    16		mutex_unlocked = 0
    17		mutex_locked   = 1
    18	
    19		note_cleared = 0
    20		note_woken   = 1
    21		note_timeout = 2
    22	
    23		active_spin     = 4
    24		active_spin_cnt = 30
    25		passive_spin    = 1
    26	)
    27	
    28	func lock(l *mutex) {
    29		if l.key == mutex_locked {
    30			// js/wasm is single-threaded so we should never
    31			// observe this.
    32			throw("self deadlock")
    33		}
    34		gp := getg()
    35		if gp.m.locks < 0 {
    36			throw("lock count")
    37		}
    38		gp.m.locks++
    39		l.key = mutex_locked
    40	}
    41	
    42	func unlock(l *mutex) {
    43		if l.key == mutex_unlocked {
    44			throw("unlock of unlocked lock")
    45		}
    46		gp := getg()
    47		gp.m.locks--
    48		if gp.m.locks < 0 {
    49			throw("lock count")
    50		}
    51		l.key = mutex_unlocked
    52	}
    53	
    54	// One-time notifications.
    55	
    56	type noteWithTimeout struct {
    57		gp       *g
    58		deadline int64
    59	}
    60	
    61	var (
    62		notes            = make(map[*note]*g)
    63		notesWithTimeout = make(map[*note]noteWithTimeout)
    64	)
    65	
    66	func noteclear(n *note) {
    67		n.key = note_cleared
    68	}
    69	
    70	func notewakeup(n *note) {
    71		// gp := getg()
    72		if n.key == note_woken {
    73			throw("notewakeup - double wakeup")
    74		}
    75		cleared := n.key == note_cleared
    76		n.key = note_woken
    77		if cleared {
    78			goready(notes[n], 1)
    79		}
    80	}
    81	
    82	func notesleep(n *note) {
    83		throw("notesleep not supported by js")
    84	}
    85	
    86	func notetsleep(n *note, ns int64) bool {
    87		throw("notetsleep not supported by js")
    88		return false
    89	}
    90	
    91	// same as runtimeĀ·notetsleep, but called on user g (not g0)
    92	func notetsleepg(n *note, ns int64) bool {
    93		gp := getg()
    94		if gp == gp.m.g0 {
    95			throw("notetsleepg on g0")
    96		}
    97	
    98		if ns >= 0 {
    99			deadline := nanotime() + ns
   100			delay := ns/1000000 + 1 // round up
   101			if delay > 1<<31-1 {
   102				delay = 1<<31 - 1 // cap to max int32
   103			}
   104	
   105			id := scheduleTimeoutEvent(delay)
   106			mp := acquirem()
   107			notes[n] = gp
   108			notesWithTimeout[n] = noteWithTimeout{gp: gp, deadline: deadline}
   109			releasem(mp)
   110	
   111			gopark(nil, nil, waitReasonSleep, traceEvNone, 1)
   112	
   113			clearTimeoutEvent(id) // note might have woken early, clear timeout
   114			mp = acquirem()
   115			delete(notes, n)
   116			delete(notesWithTimeout, n)
   117			releasem(mp)
   118	
   119			return n.key == note_woken
   120		}
   121	
   122		for n.key != note_woken {
   123			mp := acquirem()
   124			notes[n] = gp
   125			releasem(mp)
   126	
   127			gopark(nil, nil, waitReasonZero, traceEvNone, 1)
   128	
   129			mp = acquirem()
   130			delete(notes, n)
   131			releasem(mp)
   132		}
   133		return true
   134	}
   135	
   136	// checkTimeouts resumes goroutines that are waiting on a note which has reached its deadline.
   137	func checkTimeouts() {
   138		now := nanotime()
   139		for n, nt := range notesWithTimeout {
   140			if n.key == note_cleared && now >= nt.deadline {
   141				n.key = note_timeout
   142				goready(nt.gp, 1)
   143			}
   144		}
   145	}
   146	
   147	var returnedEventHandler *g
   148	
   149	func init() {
   150		// At the toplevel we need an extra goroutine that handles asynchronous events.
   151		initg := getg()
   152		go func() {
   153			returnedEventHandler = getg()
   154			goready(initg, 1)
   155	
   156			gopark(nil, nil, waitReasonZero, traceEvNone, 1)
   157			returnedEventHandler = nil
   158	
   159			pause(getcallersp() - 16)
   160		}()
   161		gopark(nil, nil, waitReasonZero, traceEvNone, 1)
   162	}
   163	
   164	// beforeIdle gets called by the scheduler if no goroutine is awake.
   165	// We resume the event handler (if available) which will pause the execution.
   166	func beforeIdle() bool {
   167		if returnedEventHandler != nil {
   168			goready(returnedEventHandler, 1)
   169			return true
   170		}
   171		return false
   172	}
   173	
   174	// pause sets SP to newsp and pauses the execution of Go's WebAssembly code until an event is triggered.
   175	func pause(newsp uintptr)
   176	
   177	// scheduleTimeoutEvent tells the WebAssembly environment to trigger an event after ms milliseconds.
   178	// It returns a timer id that can be used with clearTimeoutEvent.
   179	func scheduleTimeoutEvent(ms int64) int32
   180	
   181	// clearTimeoutEvent clears a timeout event scheduled by scheduleTimeoutEvent.
   182	func clearTimeoutEvent(id int32)
   183	
   184	func handleEvent() {
   185		prevReturnedEventHandler := returnedEventHandler
   186		returnedEventHandler = nil
   187	
   188		checkTimeouts()
   189		eventHandler()
   190	
   191		returnedEventHandler = getg()
   192		gopark(nil, nil, waitReasonZero, traceEvNone, 1)
   193	
   194		returnedEventHandler = prevReturnedEventHandler
   195	
   196		pause(getcallersp() - 16)
   197	}
   198	
   199	var eventHandler func()
   200	
   201	//go:linkname setEventHandler syscall/js.setEventHandler
   202	func setEventHandler(fn func()) {
   203		eventHandler = fn
   204	}
   205	

View as plain text