Update Marl to a089b4aaf

Changes:
    a089b4aaf Update tools/bench to handle new Google Benchmark output
    b98f00446 Use @platforms instead of @bazel_tools
    a192264a8 Update google benchmark to v1.8.0
    4be5115d8 Implement move assignment operator as move ctor already exists
    f05db401e Core is cheap to copy, pass by reference can hurt performance
    40ce132cc Cache workers in variable to speedup access
    eb2780637 Encapsulate OS stuff in Thread::Impl
    d2bae7442 Use = default ctor/dtor bodies instead of {}
    e5ffc9907 remove unnecessary html canvas in example
    38743a45a exclude example files from license check
    3c9b819ec added license header
    47d4a52ac updated copyright year
    7b1f567b8 emscripten support: updated readme
    d5f5f80f8 proper emscripten support - tested on the examples
    60d60c682 [build] make it build under emscripten - untested and most likely incorrect runtime behavior.
    99646b3c3 Fix Android host build

Commands:
    ./third_party/update-marl.sh

Bug: b/140546382
Change-Id: I6e0cecf0cbfb975dd9b6039c5700906ca63488c2
diff --git a/third_party/marl/BUILD.bazel b/third_party/marl/BUILD.bazel
index 3d95fe9..297fd44 100644
--- a/third_party/marl/BUILD.bazel
+++ b/third_party/marl/BUILD.bazel
@@ -12,6 +12,19 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+config_setting(
+    name = "linux_x86_64",
+    constraint_values = [
+        "@platforms//os:linux",
+        "@platforms//cpu:x86_64",
+    ],
+)
+
+config_setting(
+    name = "windows",
+    constraint_values = ["@platforms//os:windows"],
+)
+
 cc_library(
     name = "marl",
     srcs = glob(
@@ -25,7 +38,7 @@
             "src/**/*_test.cpp",
         ]),
     ) + select({
-        "@bazel_tools//src/conditions:windows": [],
+        ":windows": [],
         "//conditions:default": glob(["src/**/*.S"]),
     }),
     hdrs = glob([
@@ -35,7 +48,7 @@
         "include",
     ],
     linkopts = select({
-        "@bazel_tools//src/conditions:linux_x86_64": ["-pthread"],
+        ":linux_x86_64": ["-pthread"],
         "//conditions:default": [],
     }),
     visibility = [
diff --git a/third_party/marl/CMakeLists.txt b/third_party/marl/CMakeLists.txt
index 5f17ee8..78ee4a3 100644
--- a/third_party/marl/CMakeLists.txt
+++ b/third_party/marl/CMakeLists.txt
@@ -24,6 +24,10 @@
     LANGUAGES C CXX ASM
 )
 
+if (EMSCRIPTEN)
+    add_compile_options(-O3 -pthread)
+endif()
+
 include(CheckCXXSourceCompiles)
 
 # MARL_IS_SUBPROJECT is 1 if added via add_subdirectory() from another project.
@@ -171,6 +175,7 @@
         ${MARL_SRC_DIR}/osfiber_rv64.c
         ${MARL_SRC_DIR}/osfiber_x64.c
         ${MARL_SRC_DIR}/osfiber_x86.c
+        ${MARL_SRC_DIR}/osfiber_emscripten.cpp
     )
     # CMAKE_OSX_ARCHITECTURES settings aren't propagated to assembly files when
     # building for Apple platforms (https://gitlab.kitware.com/cmake/cmake/-/issues/20771),
@@ -397,6 +402,16 @@
         set_target_properties(${target} PROPERTIES FOLDER "Examples")
         marl_set_target_options(${target})
         target_link_libraries(${target} PRIVATE marl)
+        if (EMSCRIPTEN)
+            target_link_options(${target} PRIVATE
+                    -O1
+                    -pthread -sPTHREAD_POOL_SIZE=2 -sPROXY_TO_PTHREAD
+                    -sASYNCIFY # -sASYNCIFY_STACK_SIZE=1000000
+                    -sALLOW_MEMORY_GROWTH=1 -sASSERTIONS
+                    -sENVIRONMENT=web,worker
+                    "SHELL:--shell-file ${CMAKE_CURRENT_SOURCE_DIR}/examples/shell.emscripten.html")
+            set_target_properties(${target} PROPERTIES SUFFIX .html)
+        endif()
     endfunction(build_example)
 
     build_example(fractal)
diff --git a/third_party/marl/README.md b/third_party/marl/README.md
index 540be90..8c57078 100644
--- a/third_party/marl/README.md
+++ b/third_party/marl/README.md
@@ -8,7 +8,7 @@
 
 Marl uses a combination of fibers and threads to allow efficient execution of tasks that can block, while keeping a fixed number of hardware threads.
 
-Marl supports Windows, macOS, Linux, FreeBSD, Fuchsia, Android and iOS (arm, aarch64, loongarch64, mips64, ppc64, rv64, x86 and x64).
+Marl supports Windows, macOS, Linux, FreeBSD, Fuchsia, Emscripten, Android and iOS (arm, aarch64, loongarch64, mips64, ppc64, rv64, x86 and x64).
 
 Marl has no dependencies on other libraries (with an exception on googletest for building the optional unit tests).
 
@@ -93,6 +93,26 @@
 
 The resulting binaries will be found in `<path-to-marl>/build`
 
+### Emscripten
+
+1. install and activate the emscripten sdk following [standard instructions for your platform](https://emscripten.org/docs/getting_started/downloads.html).
+2. build an example from the examples folder using emscripten, say `hello_task`. 
+```bash
+cd <path-to-marl>
+mkdir build
+cd build
+emcmake cmake .. -DMARL_BUILD_EXAMPLES=1
+make hello_task -j 8
+```
+NOTE: you want to change the value of the linker flag `sPTHREAD_POOL_SIZE` that must be at least as large as the number of threads used by your application.
+3. Test the emscripten output.
+You can use the provided python script to create a local web server:
+```bash
+../run_webserver
+```
+In your browser, navigate to the example URL: [http://127.0.0.1:8080/hello_task.html](http://127.0.0.1:8080/hello_task.html).  
+Voilà - you should see the log output appear on the web page.
+
 ### Installing Marl (vcpkg)
 
 Alternatively, you can build and install Marl using [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager:
diff --git a/third_party/marl/WORKSPACE b/third_party/marl/WORKSPACE
index 60425d6..b11314c 100644
--- a/third_party/marl/WORKSPACE
+++ b/third_party/marl/WORKSPACE
@@ -12,7 +12,20 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-local_repository(
-  name = "googletest",
-  path = "./third_party/googletest",
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+http_archive(
+    name = "googletest",  # 2021-07-09
+    sha256 = "353571c2440176ded91c2de6d6cd88ddd41401d14692ec1f99e35d013feda55a",
+    strip_prefix = "googletest-release-1.11.0",
+    urls = ["https://github.com/google/googletest/archive/refs/tags/release-1.11.0.zip"],
+)
+
+http_archive(
+    name = "platforms",
+    urls = [
+        "https://mirror.bazel.build/github.com/bazelbuild/platforms/releases/download/0.0.6/platforms-0.0.6.tar.gz",
+        "https://github.com/bazelbuild/platforms/releases/download/0.0.6/platforms-0.0.6.tar.gz",
+    ],
+    sha256 = "5308fc1d8865406a49427ba24a9ab53087f17f5266a7aabbfc28823f3916e1ca",
 )
diff --git a/third_party/marl/examples/run_webserver b/third_party/marl/examples/run_webserver
new file mode 100644
index 0000000..f73442f
--- /dev/null
+++ b/third_party/marl/examples/run_webserver
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+"""
+Runs a COOE/COEP local webserver for testing emscripten deployment.
+Note:
+    Browsers that have implemented and enabled SharedArrayBuffer are gating it behind Cross Origin Opener Policy (COOP)
+    and Cross Origin Embedder Policy (COEP) headers.
+    Pthreads code will not work in deployed environment unless these headers are correctly set.
+    see: https://emscripten.org/docs/porting/pthreads.html
+"""
+
+from http.server import HTTPServer, SimpleHTTPRequestHandler
+
+
+class RequestHandler(SimpleHTTPRequestHandler):
+    def end_headers(self):
+        self.send_header("Access-Control-Allow-Origin", "*")
+        self.send_header("Cross-Origin-Embedder-Policy", "require-corp")
+        self.send_header("Cross-Origin-Opener-Policy", "same-origin")
+        super().end_headers()
+
+
+def main():
+
+    addr = "127.0.0.1"
+    port = 8080
+    httpd = HTTPServer((addr, port), RequestHandler)
+    print("Serving http at http://{}:{}".format(addr, port))
+
+    try:
+        httpd.serve_forever()
+    except KeyboardInterrupt:
+        print("\nBye.")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/third_party/marl/examples/shell.emscripten.html b/third_party/marl/examples/shell.emscripten.html
new file mode 100644
index 0000000..157e1dc
--- /dev/null
+++ b/third_party/marl/examples/shell.emscripten.html
@@ -0,0 +1,179 @@
+<!doctype html>
+<html lang="en-us">
+<head>
+    <meta charset="utf-8">
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+    <title>Emscripten-Generated Code</title>
+    <style>
+      body {
+        font-family: arial;
+        margin: 0;
+        padding: none;
+      }
+
+      .emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
+      div.emscripten { text-align: center; }
+      div.emscripten_border { border: 1px solid black; }
+
+      #emscripten_logo {
+        display: inline-block;
+        margin: 0;
+      }
+
+      .spinner {
+        height: 30px;
+        width: 30px;
+        margin: 0;
+        margin-top: 20px;
+        margin-left: 20px;
+        display: inline-block;
+        vertical-align: top;
+
+        -webkit-animation: rotation .8s linear infinite;
+        -moz-animation: rotation .8s linear infinite;
+        -o-animation: rotation .8s linear infinite;
+        animation: rotation 0.8s linear infinite;
+
+        border-left: 5px solid rgb(235, 235, 235);
+        border-right: 5px solid rgb(235, 235, 235);
+        border-bottom: 5px solid rgb(235, 235, 235);
+        border-top: 5px solid rgb(120, 120, 120);
+
+        border-radius: 100%;
+        background-color: rgb(189, 215, 46);
+      }
+
+      @-webkit-keyframes rotation {
+        from {-webkit-transform: rotate(0deg);}
+        to {-webkit-transform: rotate(360deg);}
+      }
+      @-moz-keyframes rotation {
+        from {-moz-transform: rotate(0deg);}
+        to {-moz-transform: rotate(360deg);}
+      }
+      @-o-keyframes rotation {
+        from {-o-transform: rotate(0deg);}
+        to {-o-transform: rotate(360deg);}
+      }
+      @keyframes rotation {
+        from {transform: rotate(0deg);}
+        to {transform: rotate(360deg);}
+      }
+
+      #status {
+        display: inline-block;
+        vertical-align: top;
+        margin-top: 30px;
+        margin-left: 20px;
+        font-weight: bold;
+        color: rgb(120, 120, 120);
+      }
+
+      #progress {
+        height: 20px;
+        width: 300px;
+      }
+
+      #controls {
+        display: inline-block;
+        float: right;
+        vertical-align: top;
+        margin-top: 30px;
+        margin-right: 20px;
+      }
+
+      #output {
+        width: 100%;
+        height: 200px;
+        margin: 0 auto;
+        margin-top: 10px;
+        border-left: 0px;
+        border-right: 0px;
+        padding-left: 0px;
+        padding-right: 0px;
+        display: block;
+        background-color: black;
+        color: white;
+        font-family: 'Lucida Console', Monaco, monospace;
+        outline: none;
+      }
+    </style>
+</head>
+<body>
+
+<div class="spinner" id='spinner'></div>
+<div class="emscripten" id="status">Downloading...</div>
+
+<div class="emscripten">
+    <progress value="0" max="100" id="progress" hidden=1></progress>
+</div>
+
+
+<textarea id="output" rows="8"></textarea>
+
+<script type='text/javascript'>
+      var statusElement = document.getElementById('status');
+      var progressElement = document.getElementById('progress');
+      var spinnerElement = document.getElementById('spinner');
+
+      var Module = {
+        preRun: [],
+        postRun: [],
+        print: (function() {
+          var element = document.getElementById('output');
+          if (element) element.value = ''; // clear browser cache
+          return function(text) {
+            if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
+            // These replacements are necessary if you render to raw HTML
+            //text = text.replace(/&/g, "&amp;");
+            //text = text.replace(/</g, "&lt;");
+            //text = text.replace(/>/g, "&gt;");
+            //text = text.replace('\n', '<br>', 'g');
+            console.log(text);
+            if (element) {
+              element.value += text + "\n";
+              element.scrollTop = element.scrollHeight; // focus on bottom
+            }
+          };
+        })(),
+        setStatus: function(text) {
+          if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' };
+          if (text === Module.setStatus.last.text) return;
+          var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
+          var now = Date.now();
+          if (m && now - Module.setStatus.last.time < 30) return; // if this is a progress update, skip it if too soon
+          Module.setStatus.last.time = now;
+          Module.setStatus.last.text = text;
+          if (m) {
+            text = m[1];
+            progressElement.value = parseInt(m[2])*100;
+            progressElement.max = parseInt(m[4])*100;
+            progressElement.hidden = false;
+            spinnerElement.hidden = false;
+          } else {
+            progressElement.value = null;
+            progressElement.max = null;
+            progressElement.hidden = true;
+            if (!text) spinnerElement.style.display = 'none';
+          }
+          statusElement.innerHTML = text;
+        },
+        totalDependencies: 0,
+        monitorRunDependencies: function(left) {
+          this.totalDependencies = Math.max(this.totalDependencies, left);
+          Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
+        }
+      };
+      Module.setStatus('Downloading...');
+      window.onerror = function(event) {
+        // TODO: do not warn on ok events like simulating an infinite loop or exitStatus
+        Module.setStatus('Exception thrown, see JavaScript console');
+        spinnerElement.style.display = 'none';
+        Module.setStatus = function(text) {
+          if (text) console.error('[post-exception status] ' + text);
+        };
+      };
+    </script>
+{{{ SCRIPT }}}
+</body>
+</html>
diff --git a/third_party/marl/include/marl/mutex.h b/third_party/marl/include/marl/mutex.h
index 2c9bb46..674ec33 100644
--- a/third_party/marl/include/marl/mutex.h
+++ b/third_party/marl/include/marl/mutex.h
@@ -73,7 +73,7 @@
 class SCOPED_CAPABILITY lock {
  public:
   inline lock(mutex& m) ACQUIRE(m) : _(m._) {}
-  inline ~lock() RELEASE() {}
+  inline ~lock() RELEASE() = default;
 
   // wait calls cv.wait() on this lock.
   template <typename Predicate>
diff --git a/third_party/marl/include/marl/task.h b/third_party/marl/include/marl/task.h
index 1e7d3f4..f8dd4af 100644
--- a/third_party/marl/include/marl/task.h
+++ b/third_party/marl/include/marl/task.h
@@ -61,7 +61,7 @@
   Flags flags = Flags::None;
 };
 
-Task::Task() {}
+Task::Task() = default;
 Task::Task(const Task& o) : function(o.function), flags(o.flags) {}
 Task::Task(Task&& o) : function(std::move(o.function)), flags(o.flags) {}
 Task::Task(const Function& function_, Flags flags_ /* = Flags::None */)
diff --git a/third_party/marl/include/marl/thread.h b/third_party/marl/include/marl/thread.h
index 43bb719..e72f33e 100644
--- a/third_party/marl/include/marl/thread.h
+++ b/third_party/marl/include/marl/thread.h
@@ -43,8 +43,8 @@
     };
 
     // Comparison functions
-    MARL_NO_EXPORT inline bool operator==(const Core&) const;
-    MARL_NO_EXPORT inline bool operator<(const Core&) const;
+    MARL_NO_EXPORT inline bool operator==(Core) const;
+    MARL_NO_EXPORT inline bool operator<(Core) const;
   };
 
   // Affinity holds the affinity mask for a thread - a description of what cores
@@ -93,6 +93,8 @@
 
     MARL_EXPORT Affinity(Affinity&&);
 
+    MARL_EXPORT Affinity& operator=(Affinity&&);
+
     MARL_EXPORT Affinity(const Affinity&, Allocator* allocator);
 
     // all() returns an Affinity with all the cores available to the process.
@@ -157,11 +159,11 @@
 // Thread::Core
 ////////////////////////////////////////////////////////////////////////////////
 // Comparison functions
-bool Thread::Core::operator==(const Core& other) const {
+bool Thread::Core::operator==(Core other) const {
   return pthread.index == other.pthread.index;
 }
 
-bool Thread::Core::operator<(const Core& other) const {
+bool Thread::Core::operator<(Core other) const {
   return pthread.index < other.pthread.index;
 }
 
diff --git a/third_party/marl/license-checker.cfg b/third_party/marl/license-checker.cfg
index 179d247..43bcf33 100644
--- a/third_party/marl/license-checker.cfg
+++ b/third_party/marl/license-checker.cfg
@@ -18,7 +18,9 @@
                 "docs/imgs/*.svg",
                 "kokoro/**.cfg",
                 "third_party/benchmark/**",
-                "third_party/googletest/**"
+                "third_party/googletest/**",
+                "examples/run_webserver",
+                "examples/shell.emscripten.html"
             ]
         }
     ]
diff --git a/third_party/marl/src/memory.cpp b/third_party/marl/src/memory.cpp
index a8a5bff..f52be55 100644
--- a/third_party/marl/src/memory.cpp
+++ b/third_party/marl/src/memory.cpp
@@ -19,7 +19,7 @@
 
 #include <cstring>
 
-#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(__EMSCRIPTEN__)
 #include <sys/mman.h>
 #include <unistd.h>
 namespace {
diff --git a/third_party/marl/src/osfiber_asm.h b/third_party/marl/src/osfiber_asm.h
index 5d6ce4d..7193590 100644
--- a/third_party/marl/src/osfiber_asm.h
+++ b/third_party/marl/src/osfiber_asm.h
@@ -38,6 +38,8 @@
 #include "osfiber_asm_rv64.h"
 #elif defined(__loongarch__) && _LOONGARCH_SIM == _ABILP64
 #include "osfiber_asm_loongarch64.h"
+#elif defined(__EMSCRIPTEN__)
+#include "osfiber_emscripten.h"
 #else
 #error "Unsupported target"
 #endif
@@ -50,6 +52,13 @@
 
 extern "C" {
 
+#if defined(__EMSCRIPTEN__)
+MARL_EXPORT
+void marl_main_fiber_init(marl_fiber_context* ctx);
+#else
+MARL_EXPORT
+inline void marl_main_fiber_init(marl_fiber_context*) {}
+#endif
 MARL_EXPORT
 extern void marl_fiber_set_target(marl_fiber_context*,
                                   void* stack,
@@ -108,6 +117,7 @@
     Allocator* allocator) {
   auto out = allocator->make_unique<OSFiber>(allocator);
   out->context = {};
+  marl_main_fiber_init(&out->context);
   return out;
 }
 
diff --git a/third_party/marl/src/osfiber_emscripten.cpp b/third_party/marl/src/osfiber_emscripten.cpp
new file mode 100644
index 0000000..e6caa0f
--- /dev/null
+++ b/third_party/marl/src/osfiber_emscripten.cpp
@@ -0,0 +1,60 @@
+// Copyright 2023 The Marl Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#if defined(__EMSCRIPTEN__)
+
+#include "osfiber_emscripten.h"
+
+#include "marl/export.h"
+
+
+extern "C" {
+
+MARL_EXPORT
+void marl_fiber_trampoline(void (*target)(void*), void* arg) {
+  target(arg);
+}
+
+MARL_EXPORT
+void marl_main_fiber_init(marl_fiber_context* ctx) {
+  emscripten_fiber_init_from_current_context(
+          &ctx->context,
+          ctx->asyncify_stack.data(),
+          ctx->asyncify_stack.size());
+}
+
+MARL_EXPORT
+void marl_fiber_set_target(marl_fiber_context* ctx,
+                           void* stack,
+                           uint32_t stack_size,
+                           void (*target)(void*),
+                           void* arg) {
+
+  emscripten_fiber_init(
+          &ctx->context,
+          target,
+          arg,
+          stack,
+          stack_size,
+          ctx->asyncify_stack.data(),
+          ctx->asyncify_stack.size());
+}
+
+MARL_EXPORT
+extern void marl_fiber_swap(marl_fiber_context* from,
+                            const marl_fiber_context* to) {
+  emscripten_fiber_swap(&from->context, const_cast<emscripten_fiber_t*>(&to->context));
+}
+}
+#endif  // defined(__EMSCRIPTEN__)
diff --git a/third_party/marl/src/osfiber_emscripten.h b/third_party/marl/src/osfiber_emscripten.h
new file mode 100644
index 0000000..f24eeb3
--- /dev/null
+++ b/third_party/marl/src/osfiber_emscripten.h
@@ -0,0 +1,30 @@
+// Copyright 2019 The Marl Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MARL_BUILD_WASM
+
+#include <cstdint>
+#include <cstddef>
+#include <array>
+#include <emscripten.h>
+#include <emscripten/fiber.h>
+
+struct marl_fiber_context {
+  // callee-saved data
+  static constexpr size_t asyncify_stack_size = 1024 * 1024;
+  emscripten_fiber_t context;
+  std::array</*std::byte*/ char, asyncify_stack_size> asyncify_stack;
+};
+
+#endif  // MARL_BUILD_ASM
diff --git a/third_party/marl/src/scheduler.cpp b/third_party/marl/src/scheduler.cpp
index f5e9df0..dcdf83a 100644
--- a/third_party/marl/src/scheduler.cpp
+++ b/third_party/marl/src/scheduler.cpp
@@ -114,12 +114,12 @@
   {
     marl::lock lock(get()->singleThreadedWorkers.mutex);
     auto tid = std::this_thread::get_id();
-    auto it = get()->singleThreadedWorkers.byTid.find(tid);
-    MARL_ASSERT(it != get()->singleThreadedWorkers.byTid.end(),
-                "singleThreadedWorker not found");
+    auto& workers = get()->singleThreadedWorkers.byTid;
+    auto it = workers.find(tid);
+    MARL_ASSERT(it != workers.end(), "singleThreadedWorker not found");
     MARL_ASSERT(it->second.get() == worker, "worker is not bound?");
-    get()->singleThreadedWorkers.byTid.erase(it);
-    if (get()->singleThreadedWorkers.byTid.empty()) {
+    workers.erase(it);
+    if (workers.empty()) {
       get()->singleThreadedWorkers.unbind.notify_one();
     }
   }
diff --git a/third_party/marl/src/scheduler_bench.cpp b/third_party/marl/src/scheduler_bench.cpp
index d009713..81c4d2f 100644
--- a/third_party/marl/src/scheduler_bench.cpp
+++ b/third_party/marl/src/scheduler_bench.cpp
@@ -37,7 +37,8 @@
       wg.add(numTasks);
       for (auto i = 0; i < numTasks; i++) {
         marl::schedule([=] {
-          benchmark::DoNotOptimize(doSomeWork(i));
+          uint32_t value = doSomeWork(i);
+          benchmark::DoNotOptimize(value);
           wg.done();
         });
       }
@@ -58,7 +59,8 @@
       wg.add(numTasks);
       for (auto i = 0; i < numTasks; i++) {
         marl::schedule([=] {
-          benchmark::DoNotOptimize(doSomeWork(i));
+          uint32_t value = doSomeWork(i);
+          benchmark::DoNotOptimize(value);
           wg.done();
         });
       }
diff --git a/third_party/marl/src/thread.cpp b/third_party/marl/src/thread.cpp
index c6b34ae..3a275c9 100644
--- a/third_party/marl/src/thread.cpp
+++ b/third_party/marl/src/thread.cpp
@@ -50,7 +50,7 @@
 namespace {
 
 struct CoreHasher {
-  inline uint64_t operator()(const marl::Thread::Core& core) const {
+  inline uint64_t operator()(marl::Thread::Core core) const {
     return core.pthread.index;
   }
 };
@@ -117,6 +117,10 @@
 
 Thread::Affinity::Affinity(Allocator* allocator) : cores(allocator) {}
 Thread::Affinity::Affinity(Affinity&& other) : cores(std::move(other.cores)) {}
+Thread::Affinity& Thread::Affinity::operator=(Affinity&& other) {
+  cores = std::move(other.cores);
+  return *this;
+}
 Thread::Affinity::Affinity(const Affinity& other, Allocator* allocator)
     : cores(other.cores, allocator) {}
 
@@ -274,14 +278,33 @@
 
 class Thread::Impl {
  public:
-  Impl(Func&& func) : func(std::move(func)) {}
+  Impl(Func&& func, _PROC_THREAD_ATTRIBUTE_LIST* attributes)
+      : func(std::move(func)),
+        handle(CreateRemoteThreadEx(GetCurrentProcess(),
+                                    nullptr,
+                                    0,
+                                    &Impl::run,
+                                    this,
+                                    0,
+                                    attributes,
+                                    nullptr)) {}
+  ~Impl() { CloseHandle(handle); }
+
+  Impl(const Impl&) = delete;
+  Impl(Impl&&) = delete;
+  Impl& operator=(const Impl&) = delete;
+  Impl& operator=(Impl&&) = delete;
+
+  void Join() const { WaitForSingleObject(handle, INFINITE); }
+
   static DWORD WINAPI run(void* self) {
     reinterpret_cast<Impl*>(self)->func();
     return 0;
   }
 
-  Func func;
-  HANDLE handle;
+ private:
+  const Func func;
+  const HANDLE handle;
 };
 
 Thread::Thread(Affinity&& affinity, Func&& func) {
@@ -312,21 +335,16 @@
         sizeof(groupAffinity), nullptr, nullptr));
   }
 
-  impl = new Impl(std::move(func));
-  impl->handle = CreateRemoteThreadEx(GetCurrentProcess(), nullptr, 0,
-                                      &Impl::run, impl, 0, attributes, nullptr);
+  impl = new Impl(std::move(func), attributes);
 }
 
 Thread::~Thread() {
-  if (impl) {
-    CloseHandle(impl->handle);
-    delete impl;
-  }
+  delete impl;
 }
 
 void Thread::join() {
   MARL_ASSERT(impl != nullptr, "join() called on unjoinable thread");
-  WaitForSingleObject(impl->handle, INFINITE);
+  impl->Join();
 }
 
 void Thread::setName(const char* fmt, ...) {
@@ -426,7 +444,7 @@
   pthread_setname_np(name);
 #elif defined(__FreeBSD__)
   pthread_set_name_np(pthread_self(), name);
-#elif !defined(__Fuchsia__)
+#elif !defined(__Fuchsia__) && !defined(__EMSCRIPTEN__)
   pthread_setname_np(pthread_self(), name);
 #endif
 
diff --git a/third_party/marl/tools/bench/bench.go b/third_party/marl/tools/bench/bench.go
index ad81181..fccba5b 100644
--- a/third_party/marl/tools/bench/bench.go
+++ b/third_party/marl/tools/bench/bench.go
@@ -78,10 +78,10 @@
 }
 
 var errWrongFormat = errors.New("Wrong format")
-var consoleLineRE = regexp.MustCompile(`([\w/:]+)\s+([0-9]+(?:.[0-9]+)?) ns\s+[0-9]+(?:.[0-9]+) ns\s+([0-9]+)`)
+var consoleLineRE = regexp.MustCompile(`([\w/:]+)\s+([0-9]+(?:.[0-9e+]+)?) ns\s+[0-9]+(?:.[0-9e+]+) ns\s+([0-9]+)`)
 
 func parseConsole(s string) (Benchmark, error) {
-	blocks := strings.Split(s, "------------------------------------------------------------------------------------------")
+	blocks := strings.Split(s, "--------------------------------------------------------------------------------------------------------")
 	if len(blocks) != 3 {
 		return Benchmark{}, errWrongFormat
 	}