// +build js

package sync

import "github.com/gopherjs/gopherjs/js"

var semWaiters = make(map[*uint32][]chan bool)

// semAwoken tracks the number of waiters awoken by runtime_Semrelease (`ch <- true`)
// that have not yet acquired the semaphore (`<-ch` in runtime_SemacquireMutex).
//
// This prevents a new call to runtime_SemacquireMutex to wrongly acquire the semaphore
// in between (because runtime_Semrelease has already incremented the semaphore while
// all the pending calls to runtime_SemacquireMutex have not yet received from the channel
// and thus decremented the semaphore).
//
// See https://github.com/gopherjs/gopherjs/issues/736.
var semAwoken = make(map[*uint32]uint32)

func runtime_Semacquire(s *uint32) {
	runtime_SemacquireMutex(s, false)
}

// SemacquireMutex is like Semacquire, but for profiling contended Mutexes.
// Mutex profiling is not supported, so just use the same implementation as runtime_Semacquire.
// TODO: Investigate this. If it's possible to implement, consider doing so, otherwise remove this comment.
func runtime_SemacquireMutex(s *uint32, lifo bool) {
	if (*s - semAwoken[s]) == 0 {
		ch := make(chan bool)
		if lifo {
			semWaiters[s] = append([]chan bool{ch}, semWaiters[s]...)
		} else {
			semWaiters[s] = append(semWaiters[s], ch)
		}
		<-ch
		semAwoken[s] -= 1
		if semAwoken[s] == 0 {
			delete(semAwoken, s)
		}
	}
	*s--
}

func runtime_Semrelease(s *uint32, handoff bool) {
	// TODO: Use handoff if needed/possible.
	*s++

	w := semWaiters[s]
	if len(w) == 0 {
		return
	}

	ch := w[0]
	w = w[1:]
	semWaiters[s] = w
	if len(w) == 0 {
		delete(semWaiters, s)
	}

	semAwoken[s] += 1

	ch <- true
}

func runtime_notifyListCheck(size uintptr) {}

func runtime_canSpin(i int) bool {
	return false
}

// Copy of time.runtimeNano.
func runtime_nanotime() int64 {
	const millisecond = 1000000
	return js.Global.Get("Date").New().Call("getTime").Int64() * millisecond
}

// Implemented in runtime.
func throw(s string) {
	js.Global.Call("$throwRuntimeError", s)
}
