summaryrefslogtreecommitdiff
path: root/js/src/builtin/AtomicsObject.h
blob: 6e519cfa4a341487cf4ace144657b0386c604c34 (plain)
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
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef builtin_AtomicsObject_h
#define builtin_AtomicsObject_h

#include "mozilla/Maybe.h"
#include "mozilla/TimeStamp.h"

#include "jsobj.h"

#include "threading/ConditionVariable.h"
#include "vm/MutexIDs.h"

namespace js {

class AtomicsObject : public JSObject
{
  public:
    static const Class class_;
    static JSObject* initClass(JSContext* cx, Handle<GlobalObject*> global);
    [[nodiscard]] static bool toString(JSContext* cx, unsigned int argc, Value* vp);
};

[[nodiscard]] bool atomics_compareExchange(JSContext* cx, unsigned argc, Value* vp);
[[nodiscard]] bool atomics_exchange(JSContext* cx, unsigned argc, Value* vp);
[[nodiscard]] bool atomics_load(JSContext* cx, unsigned argc, Value* vp);
[[nodiscard]] bool atomics_store(JSContext* cx, unsigned argc, Value* vp);
[[nodiscard]] bool atomics_add(JSContext* cx, unsigned argc, Value* vp);
[[nodiscard]] bool atomics_sub(JSContext* cx, unsigned argc, Value* vp);
[[nodiscard]] bool atomics_and(JSContext* cx, unsigned argc, Value* vp);
[[nodiscard]] bool atomics_or(JSContext* cx, unsigned argc, Value* vp);
[[nodiscard]] bool atomics_xor(JSContext* cx, unsigned argc, Value* vp);
[[nodiscard]] bool atomics_isLockFree(JSContext* cx, unsigned argc, Value* vp);
[[nodiscard]] bool atomics_wait(JSContext* cx, unsigned argc, Value* vp);
[[nodiscard]] bool atomics_notify(JSContext* cx, unsigned argc, Value* vp);

/* asm.js callouts */
namespace wasm { class Instance; }
int32_t atomics_add_asm_callout(wasm::Instance* i, int32_t vt, int32_t offset, int32_t value);
int32_t atomics_sub_asm_callout(wasm::Instance* i, int32_t vt, int32_t offset, int32_t value);
int32_t atomics_and_asm_callout(wasm::Instance* i, int32_t vt, int32_t offset, int32_t value);
int32_t atomics_or_asm_callout(wasm::Instance* i, int32_t vt, int32_t offset, int32_t value);
int32_t atomics_xor_asm_callout(wasm::Instance* i, int32_t vt, int32_t offset, int32_t value);
int32_t atomics_cmpxchg_asm_callout(wasm::Instance* i, int32_t vt, int32_t offset, int32_t oldval, int32_t newval);
int32_t atomics_xchg_asm_callout(wasm::Instance* i, int32_t vt, int32_t offset, int32_t value);

class FutexRuntime
{
    friend class AutoLockFutexAPI;

public:
    [[nodiscard]] static bool initialize();
    static void destroy();

    static void lock();
    static void unlock();

    FutexRuntime();
    [[nodiscard]] bool initInstance();
    void destroyInstance();

    // Parameters to notify().
    enum NotifyReason {
        NotifyExplicit,           // Being asked to wake up by another thread
        NotifyForJSInterrupt      // Interrupt requested
    };

    // Result code from wait().
    enum WaitResult {
        FutexOK,
        FutexTimedOut
    };

    // Block the calling thread and wait.
    //
    // The futex lock must be held around this call.
    //
    // The timeout is the number of milliseconds, with fractional
    // times allowed; specify mozilla::Nothing() for an indefinite
    // wait.
    //
    // wait() will not wake up spuriously.
    [[nodiscard]] bool wait(JSContext* cx, js::UniqueLock<js::Mutex>& locked,
                            mozilla::Maybe<mozilla::TimeDuration>& timeout, WaitResult* result);

    // Notify the thread represented by this Runtime.
    //
    // The futex lock must be held around this call.  (The sleeping
    // thread will not wake up until the caller of Atomics.notify()
    // releases the lock.)
    //
    // If the thread is not waiting then this method does nothing.
    //
    // If the thread is waiting in a call to wait() and the
    // reason is NotifyExplicit then the wait() call will return
    // with Woken.
    //
    // If the thread is waiting in a call to wait() and the
    // reason is NotifyForJSInterrupt then the wait() will return
    // with WaitingNotifiedForInterrupt; in the latter case the caller
    // of wait() must handle the interrupt.
    void notify(NotifyReason reason);

    bool isWaiting();

    // If canWait() returns false (the default) then wait() is disabled
    // on the runtime to which the FutexRuntime belongs.
    bool canWait() {
        return canWait_;
    }

    void setCanWait(bool flag) {
        canWait_ = flag;
    }

  private:
    enum FutexState {
        Idle,                        // We are not waiting or woken
        Waiting,                     // We are waiting, nothing has happened yet
        WaitingNotifiedForInterrupt, // We are waiting, but have been interrupted,
                                     //   and have not yet started running the
                                     //   interrupt handler
        WaitingInterrupted,          // We are waiting, but have been interrupted
                                     //   and are running the interrupt handler
        Woken                        // Woken by a script call to Atomics.notify
    };

    // Condition variable that this runtime will wait on.
    js::ConditionVariable* cond_;

    // Current futex state for this runtime.  When not in a wait this
    // is Idle; when in a wait it is Waiting or the reason the futex
    // is about to wake up.
    FutexState state_;

    // Shared futex lock for all runtimes.  We can perhaps do better,
    // but any lock will need to be per-domain (consider SharedWorker)
    // or coarser.
    static mozilla::Atomic<js::Mutex*> lock_;

    // A flag that controls whether waiting is allowed.
    bool canWait_;
};

JSObject*
InitAtomicsClass(JSContext* cx, HandleObject obj);

}  /* namespace js */

#endif /* builtin_AtomicsObject_h */