summaryrefslogtreecommitdiff
path: root/src/mongo/scripting/mozjs/countdownlatch.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/scripting/mozjs/countdownlatch.cpp')
-rw-r--r--src/mongo/scripting/mozjs/countdownlatch.cpp198
1 files changed, 198 insertions, 0 deletions
diff --git a/src/mongo/scripting/mozjs/countdownlatch.cpp b/src/mongo/scripting/mozjs/countdownlatch.cpp
new file mode 100644
index 00000000000..bf7ae4ff551
--- /dev/null
+++ b/src/mongo/scripting/mozjs/countdownlatch.cpp
@@ -0,0 +1,198 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/countdownlatch.h"
+
+#include <unordered_map>
+
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/objectwrapper.h"
+#include "mongo/stdx/condition_variable.h"
+#include "mongo/stdx/mutex.h"
+
+namespace mongo {
+namespace mozjs {
+
+const char* const CountDownLatchInfo::className = "CountDownLatch";
+
+const JSFunctionSpec CountDownLatchInfo::methods[5] = {
+ MONGO_ATTACH_JS_FUNCTION(_new),
+ MONGO_ATTACH_JS_FUNCTION(_await),
+ MONGO_ATTACH_JS_FUNCTION(_countDown),
+ MONGO_ATTACH_JS_FUNCTION(_getCount),
+ JS_FS_END,
+};
+
+/**
+ * The global CountDownLatch holder.
+ *
+ * Provides an interface for communicating between JSThread's
+ */
+class CountDownLatchHolder {
+public:
+ CountDownLatchHolder() : _counter(0) {}
+
+ int32_t make(int32_t count) {
+ uassert(ErrorCodes::JSInterpreterFailure, "argument must be >= 0", count >= 0);
+ stdx::lock_guard<stdx::mutex> lock(_mutex);
+
+ int32_t desc = ++_counter;
+ _latches.insert(std::make_pair(desc, std::make_shared<Latch>(count)));
+
+ return desc;
+ }
+
+ void await(int32_t desc) {
+ std::shared_ptr<Latch> latch = get(desc);
+ stdx::unique_lock<stdx::mutex> lock(latch->mutex);
+
+ while (latch->count != 0) {
+ latch->cv.wait(lock);
+ }
+ }
+
+ void countDown(int32_t desc) {
+ std::shared_ptr<Latch> latch = get(desc);
+ stdx::unique_lock<stdx::mutex> lock(latch->mutex);
+
+ if (latch->count > 0)
+ latch->count--;
+
+ if (latch->count == 0)
+ latch->cv.notify_all();
+ }
+
+ int32_t getCount(int32_t desc) {
+ std::shared_ptr<Latch> latch = get(desc);
+ stdx::unique_lock<stdx::mutex> lock(latch->mutex);
+
+ return latch->count;
+ }
+
+private:
+ /**
+ * Latches for communication between threads
+ */
+ struct Latch {
+ Latch(int32_t count) : count(count) {}
+
+ stdx::mutex mutex;
+ stdx::condition_variable cv;
+ int32_t count;
+ };
+
+ std::shared_ptr<Latch> get(int32_t desc) {
+ stdx::lock_guard<stdx::mutex> lock(_mutex);
+
+ auto iter = _latches.find(desc);
+ uassert(ErrorCodes::JSInterpreterFailure,
+ "not a valid CountDownLatch descriptor",
+ iter != _latches.end());
+
+ return iter->second;
+ }
+
+ using Map = std::unordered_map<int32_t, std::shared_ptr<Latch>>;
+
+ stdx::mutex _mutex;
+ Map _latches;
+ int32_t _counter;
+};
+
+namespace {
+CountDownLatchHolder globalCountDownLatchHolder;
+} // namespace
+
+void CountDownLatchInfo::Functions::_new(JSContext* cx, JS::CallArgs args) {
+ uassert(ErrorCodes::JSInterpreterFailure, "need exactly one argument", args.length() == 1);
+ uassert(
+ ErrorCodes::JSInterpreterFailure, "argument must be an integer", args.get(0).isNumber());
+
+ args.rval().setInt32(globalCountDownLatchHolder.make(args.get(0).toNumber()));
+}
+
+void CountDownLatchInfo::Functions::_await(JSContext* cx, JS::CallArgs args) {
+ uassert(ErrorCodes::JSInterpreterFailure, "need exactly one argument", args.length() == 1);
+ uassert(
+ ErrorCodes::JSInterpreterFailure, "argument must be an integer", args.get(0).isNumber());
+
+ globalCountDownLatchHolder.await(args.get(0).toNumber());
+
+ args.rval().setUndefined();
+}
+
+void CountDownLatchInfo::Functions::_countDown(JSContext* cx, JS::CallArgs args) {
+ uassert(ErrorCodes::JSInterpreterFailure, "need exactly one argument", args.length() == 1);
+ uassert(
+ ErrorCodes::JSInterpreterFailure, "argument must be an integer", args.get(0).isNumber());
+
+ globalCountDownLatchHolder.countDown(args.get(0).toNumber());
+
+ args.rval().setUndefined();
+}
+
+void CountDownLatchInfo::Functions::_getCount(JSContext* cx, JS::CallArgs args) {
+ uassert(ErrorCodes::JSInterpreterFailure, "need exactly one argument", args.length() == 1);
+ uassert(
+ ErrorCodes::JSInterpreterFailure, "argument must be an integer", args.get(0).isNumber());
+
+ args.rval().setInt32(globalCountDownLatchHolder.getCount(args.get(0).toNumber()));
+}
+
+/**
+ * We have to do this odd dance here because we need the methods from
+ * CountDownLatch to be installed in a plain object as enumerable properties.
+ * This is due to the way CountDownLatch is invoked, specifically after being
+ * transmitted across our js fork(). So we can't inherit and can't rely on the
+ * type. Practically, we also end up wrapping up all of these functions in pure
+ * js variants that call down, which makes them bson <-> js safe.
+ */
+void CountDownLatchInfo::postInstall(JSContext* cx,
+ JS::HandleObject global,
+ JS::HandleObject proto) {
+ auto objPtr = JS_NewPlainObject(cx);
+ uassert(ErrorCodes::JSInterpreterFailure, "Failed to JS_NewPlainObject", objPtr);
+
+ JS::RootedObject obj(cx, objPtr);
+ ObjectWrapper objWrapper(cx, obj);
+ ObjectWrapper protoWrapper(cx, proto);
+
+ JS::RootedValue val(cx);
+ for (auto iter = methods; iter->name; ++iter) {
+ protoWrapper.getValue(iter->name, &val);
+ objWrapper.setValue(iter->name, val);
+ }
+
+ val.setObjectOrNull(obj);
+ ObjectWrapper(cx, global).setValue("CountDownLatch", val);
+}
+
+} // namespace mozjs
+} // namespace mongo