In issue #790, Timmons C. Player discovered something dangerous with
on-stack sched::thread objects: The stack may be mmap()ed, so the scheduler
now needs to access an application's mmap()ed memory area. Those accesses
can potentially have all sort of problems (like page faults in the scheduler,
if it weren't for issue #143), but more concretely: The "lazy TLB flush"
code added in commit 7e38453 means that the scheduler may see old pages
for application-mmap()ed pages, so it cannot work with a sched::thread in an
mmap()ed area.

This patch prevents on-stack sched::thread construction by hiding the
constructor, and only allowing a thread to be created through the function
sched::thread::make(...), which creates the object on the heap.

This patch is long but it more-or-less contains just the following
types of changes:

1. The change to sched.hh to hide the sched::thread constructor, and
   instead offer a sched::thread::make(...) function.

2. Every class which used a "sched::thread" member was replaced by
   a std::unique_ptr<sched::thread> member.

   One "casualty" of this change is that a class that used to hold a
   sched::thread member will now hold a sched::thread pointer, and use an
   extra allocation and indirection - even if we know for sure that the
   containing object will always be allocated on the heap (such as the
   case in pthread.cc's pthread::_thread, for example). This can be worked
   around by making the exceptional class a friend of sched::thread to be
   able to use the hidden constructor - but for now I decided the tiny
   performance benefit isn't important enough to make this exception.

3. Every place which used "new sched::thread(...)" to create the thread,
   now uses "sched::thread::make(...)" instead.

   Sadly we no longer allow new sched::thread(...), although there is
   nothing wrong with it, because disabling the constructor also disables
   the new expression.

4. One test in misc-wake.cc (which wasn't enabled by default anyway)
   was commented out because it relied on creating thread objects in
   mmap()ed areas.

One of the places modified is rcu_flush(), whose on-stack sched::thread
objects are what caused issue #790.

Fixes #790.

Signed-off-by: Nadav Har'El <n...@scylladb.com>
---
 drivers/virtio-net.hh         |  6 ++--
 drivers/virtio-rng.hh         |  2 +-
 drivers/vmxnet3.hh            |  4 +--
 include/osv/mempool.hh        |  2 +-
 include/osv/percpu_xmit.hh    |  2 +-
 include/osv/sched.hh          | 12 ++++++++
 tests/tst-bsd-synch.hh        | 12 ++++----
 tests/tst-malloc.hh           |  4 +--
 tests/tst-rwlock.hh           |  8 ++---
 tests/tst-threads.hh          |  4 +--
 tests/tst-timer.hh            |  2 +-
 arch/x64/xen_intr.cc          |  2 +-
 bsd/porting/callout.cc        |  2 +-
 bsd/porting/kthread.cc        |  4 +--
 bsd/sys/net/netisr1.cc        |  2 +-
 core/async.cc                 | 12 ++++----
 core/dhcp.cc                  |  2 +-
 core/mempool.cc               | 28 ++++++++---------
 core/pagecache.cc             |  6 ++--
 core/percpu-worker.cc         |  2 +-
 core/rcu.cc                   | 20 ++++++-------
 core/sched.cc                 | 26 ++++++++--------
 core/trace.cc                 |  8 ++---
 drivers/acpi.cc               | 12 ++++----
 drivers/ahci.cc               |  2 +-
 drivers/console-driver.cc     |  2 +-
 drivers/virtio-assign.cc      |  2 +-
 drivers/virtio-blk.cc         |  2 +-
 drivers/virtio-net.cc         |  2 +-
 drivers/virtio-rng.cc         |  6 ++--
 drivers/virtio-scsi.cc        |  2 +-
 drivers/vmw-pvscsi.cc         |  2 +-
 drivers/vmxnet3.cc            |  4 +--
 libc/pthread.cc               | 28 ++++++++---------
 libc/signal.cc                |  4 +--
 libc/timerfd.cc               | 10 +++----
 tests/misc-free-perf.cc       |  2 +-
 tests/misc-leak.cc            |  4 +--
 tests/misc-lfring.cc          | 12 ++++----
 tests/misc-loadbalance.cc     |  2 +-
 tests/misc-mutex.cc           |  2 +-
 tests/misc-sockets.cc         |  8 ++---
 tests/misc-tcp-hash-srv.cc    |  2 +-
 tests/misc-wake.cc            |  2 ++
 tests/tst-af-local.cc         | 16 +++++-----
 tests/tst-bsd-tcp1-zrcv.cc    |  4 +--
 tests/tst-bsd-tcp1-zsnd.cc    |  4 +--
 tests/tst-bsd-tcp1-zsndrcv.cc |  4 +--
 tests/tst-bsd-tcp1.cc         |  4 +--
 tests/tst-condvar.cc          | 10 +++----
 tests/tst-fpu.cc              |  6 ++--
 tests/tst-mmap.cc             |  4 +--
 tests/tst-pin.cc              | 70 +++++++++++++++++++++----------------------
 tests/tst-preempt.cc          |  2 +-
 tests/tst-rcu-hashtable.cc    |  2 +-
 tests/tst-rcu-list.cc         |  2 +-
 tests/tst-threadcomplete.cc   |  4 +--
 tests/tst-vfs.cc              |  2 +-
 tests/tst-wait-for.cc         | 30 +++++++++----------
 tests/tst-yield.cc            |  2 +-
 60 files changed, 232 insertions(+), 218 deletions(-)

diff --git a/drivers/virtio-net.hh b/drivers/virtio-net.hh
index cf2d6f2..ad323e6 100644
--- a/drivers/virtio-net.hh
+++ b/drivers/virtio-net.hh
@@ -302,10 +302,10 @@ private:
     /* Single Rx queue object */
     struct rxq {
         rxq(vring* vq, std::function<void ()> poll_func)
-            : vqueue(vq), poll_task(poll_func, sched::thread::attr().
-                                    name("virtio-net-rx")) {};
+            : vqueue(vq), poll_task(sched::thread::make(poll_func, 
sched::thread::attr().
+                                    name("virtio-net-rx"))) {};
         vring* vqueue;
-        sched::thread  poll_task;
+        std::unique_ptr<sched::thread> poll_task;
         struct rxq_stats stats = { 0 };
 
         void update_wakeup_stats(const u64 wakeup_packets) {
diff --git a/drivers/virtio-rng.hh b/drivers/virtio-rng.hh
index b2d13e5..1790665 100644
--- a/drivers/virtio-rng.hh
+++ b/drivers/virtio-rng.hh
@@ -45,7 +45,7 @@ private:
     static const size_t _pool_size = 64;
     std::vector<char> _entropy;
     pci_interrupt _irq;
-    sched::thread _thread;
+    std::unique_ptr<sched::thread> _thread;
     condvar _producer;
     condvar _consumer;
     vring* _queue;
diff --git a/drivers/vmxnet3.hh b/drivers/vmxnet3.hh
index c69b4ed..1e6dc21 100644
--- a/drivers/vmxnet3.hh
+++ b/drivers/vmxnet3.hh
@@ -220,7 +220,7 @@ private:
 class vmxnet3_rxqueue : public vmxnet3_rxq_shared {
 public:
     explicit vmxnet3_rxqueue()
-    : task([this] { receive_work(); }, 
sched::thread::attr().name("vmxnet3-receive")) {};
+    : task(sched::thread::make([this] { receive_work(); }, 
sched::thread::attr().name("vmxnet3-receive"))) {};
     void init(struct ifnet* ifn, pci::bar *bar0);
     void set_intr_idx(unsigned idx) { layout->intr_idx = static_cast<u8>(idx); 
}
     void enable_interrupt();
@@ -235,7 +235,7 @@ public:
         u64 rx_bh_wakeups; /* number of timer Rx BH has been woken up */
         wakeup_stats rx_wakeup_stats;
     } stats = { 0 };
-    sched::thread task;
+    std::unique_ptr<sched::thread> task;
 
 private:
     void receive_work();
diff --git a/include/osv/mempool.hh b/include/osv/mempool.hh
index 4f36c16..5dae764 100644
--- a/include/osv/mempool.hh
+++ b/include/osv/mempool.hh
@@ -166,7 +166,7 @@ private:
 
     reclaimer_waiters _oom_blocked; // Callers are blocked due to lack of 
memory
     condvar _blocked;     // The reclaimer itself is blocked waiting for 
pressure condition
-    sched::thread _thread;
+    std::unique_ptr<sched::thread> _thread;
 
     std::vector<shrinker *> _shrinkers;
     mutex _shrinkers_mutex;
diff --git a/include/osv/percpu_xmit.hh b/include/osv/percpu_xmit.hh
index 85fd94b..952b9b2 100644
--- a/include/osv/percpu_xmit.hh
+++ b/include/osv/percpu_xmit.hh
@@ -214,7 +214,7 @@ public:
             _all_cpuqs.push_back(_cpuq.for_cpu(c)->get());
 
             _worker.for_cpu(c)->me =
-                new sched::thread([this] { poll_until(); },
+                sched::thread::make([this] { poll_until(); },
                                sched::thread::attr().pin(c).
                                name(worker_name_base + std::to_string(c->id)));
             _worker.for_cpu(c)->me->
diff --git a/include/osv/sched.hh b/include/osv/sched.hh
index 261bcfd..de9c308 100644
--- a/include/osv/sched.hh
+++ b/include/osv/sched.hh
@@ -399,8 +399,20 @@ public:
         terminated,
     };
 
+    // New threads must be created by the sched::make() function, which
+    // creates the thread structure on the heap. The constructor is made
+    // private so that thread objects *cannot* be created on the stack.
+    // Stacks might be mmap'ed, and the scheduler might not see an up-to-date
+    // view of the application's page table (see issue #790).
+    template <typename... Args>
+    static thread* make(Args&&... args) {
+        return new thread(std::forward<Args>(args)...);
+    }
+private:
     explicit thread(std::function<void ()> func, attr attributes = attr(),
             bool main = false, bool app = false);
+
+public:
     ~thread();
     void start();
     template <class Pred>
diff --git a/tests/tst-bsd-synch.hh b/tests/tst-bsd-synch.hh
index 011035e..ebfd2be 100644
--- a/tests/tst-bsd-synch.hh
+++ b/tests/tst-bsd-synch.hh
@@ -34,13 +34,13 @@ public:
 
         sched::thread::attr a;
         a.detached();
-        sched::thread *t21 = new sched::thread([this] {
+        sched::thread *t21 = sched::thread::make([this] {
             dbg_d("t21-before");
             msleep((void*)567, NULL, 0, "test1", 0);
             dbg_d("t21-after");
         }, a);
 
-        sched::thread *t22 = new sched::thread([this] {
+        sched::thread *t22 = sched::thread::make([this] {
             dbg_d("t22-before");
             msleep((void*)567, NULL, 0, "test1", 0);
             dbg_d("t22-after");
@@ -71,13 +71,13 @@ public:
 
         sched::thread::attr a;
         a.detached();
-        sched::thread *t11 = new sched::thread([this] {
+        sched::thread *t11 = sched::thread::make([this] {
             dbg_d("t11-before");
             msleep((void*)123, NULL, 0, "test1", 0);
             dbg_d("t11-after");
         }, a);
 
-        sched::thread *t12 = new sched::thread([this] {
+        sched::thread *t12 = sched::thread::make([this] {
             dbg_d("t12-before");
             msleep((void*)123, NULL, 0, "test1", 0);
             dbg_d("t12-after");
@@ -101,10 +101,10 @@ public:
         // Run the tests in detached threads
         sched::thread::attr a;
         a.detached();
-        sched::thread *t1 = new sched::thread([this] { test1(); }, a);
+        sched::thread *t1 = sched::thread::make([this] { test1(); }, a);
         t1->start();
 
-        sched::thread *t2 = new sched::thread([this] { test2(); }, a);
+        sched::thread *t2 = sched::thread::make([this] { test2(); }, a);
         t2->start();
 #endif
     }
diff --git a/tests/tst-malloc.hh b/tests/tst-malloc.hh
index fe15218..64041a0 100644
--- a/tests/tst-malloc.hh
+++ b/tests/tst-malloc.hh
@@ -88,8 +88,8 @@ public:
     {
         test_locks t;
         t.die = t.free_finished = t.alloc_finished = false;
-        sched::thread* t1 = new sched::thread([&] { alloc_thread(t); });
-        sched::thread* t2 = new sched::thread([&] { free_thread(t); });
+        sched::thread* t1 = sched::thread::make([&] { alloc_thread(t); });
+        sched::thread* t2 = sched::thread::make([&] { free_thread(t); });
         t1->start();
         t2->start();
 
diff --git a/tests/tst-rwlock.hh b/tests/tst-rwlock.hh
index d60ee1f..86ca1f2 100644
--- a/tests/tst-rwlock.hh
+++ b/tests/tst-rwlock.hh
@@ -133,7 +133,7 @@ public:
 
         // Test 1
         _test1_finished = false;
-        thread* t1 = new thread([&] { rwlock_test1(); });
+        thread* t1 = thread::make([&] { rwlock_test1(); });
         t1->start();
         _main->wait_until([&] { return (_test1_finished); });
         delete t1;
@@ -142,8 +142,8 @@ public:
         _test2_t1_finished = false;
         _test2_t2_finished = false;
         rw_init(&_test2_rwlock, "tst2");
-        thread* t2_1 = new thread([&] { rwlock_test2_t1(); });
-        thread* t2_2 = new thread([&] { rwlock_test2_t2(); });
+        thread* t2_1 = thread::make([&] { rwlock_test2_t1(); });
+        thread* t2_2 = thread::make([&] { rwlock_test2_t2(); });
         t2_1->start();
         t2_2->start();
         _main->wait_until([&] { return (_test2_t1_finished && 
_test2_t2_finished); });
@@ -152,7 +152,7 @@ public:
 
         // Test 3
         _test3_finished = false;
-        thread* t3 = new thread([&] { rwlock_test3(); });
+        thread* t3 = thread::make([&] { rwlock_test3(); });
         t3->start();
         _main->wait_until([&] { return (_test3_finished); });
         delete t3;
diff --git a/tests/tst-threads.hh b/tests/tst-threads.hh
index fb7d445..7df4124 100644
--- a/tests/tst-threads.hh
+++ b/tests/tst-threads.hh
@@ -55,8 +55,8 @@ public:
         test_threads_data tt;
         tt.main = sched::thread::current();
         tt.t1ok = tt.t2ok = true;
-        tt.t1 = new sched::thread([&] { test_thread_1(tt); });
-        tt.t2 = new sched::thread([&] { test_thread_2(tt); });
+        tt.t1 = sched::thread::make([&] { test_thread_1(tt); });
+        tt.t2 = sched::thread::make([&] { test_thread_2(tt); });
         tt.test_ctr = 0;
         tt.t1->start();
         tt.t2->start();
diff --git a/tests/tst-timer.hh b/tests/tst-timer.hh
index 8038bb3..d99c99a 100644
--- a/tests/tst-timer.hh
+++ b/tests/tst-timer.hh
@@ -70,7 +70,7 @@ public:
     {
         debug("Starting stress test\n");
         for (int i=0; i<max_testers; i++) {
-            _testers[i] = new sched::thread([&] { this->stress_thread(); });
+            _testers[i] = sched::thread::make([&] { this->stress_thread(); });
             _testers[i]->start();
         }
 
diff --git a/arch/x64/xen_intr.cc b/arch/x64/xen_intr.cc
index c7f7126..5fd25c2 100644
--- a/arch/x64/xen_intr.cc
+++ b/arch/x64/xen_intr.cc
@@ -106,7 +106,7 @@ void xen_irq::do_irq()
 
 void xen_irq::_cpu_init(sched::cpu *c)
 {
-    *(_thread.for_cpu(c)) = new sched::thread([this] { xen_irq::do_irq(); },
+    *(_thread.for_cpu(c)) = sched::thread::make([this] { xen_irq::do_irq(); },
             sched::thread::attr().pin(c).name(osv::sprintf("xenirq%d", 
c->id)));
     (*(_thread.for_cpu(c)))->start();
 }
diff --git a/bsd/porting/callout.cc b/bsd/porting/callout.cc
index 889d962..d8bce23 100644
--- a/bsd/porting/callout.cc
+++ b/bsd/porting/callout.cc
@@ -323,7 +323,7 @@ void callout_init_mtx(struct callout *c, struct mtx *mtx, 
int flags)
 void init_callouts(void)
 {
     // Start the callout thread
-    callouts::_callout_dispatcher = new sched::thread(_callout_thread,
+    callouts::_callout_dispatcher = sched::thread::make(_callout_thread,
             sched::thread::attr().name("callout"));
     callouts::_callout_dispatcher->start();
 }
diff --git a/bsd/porting/kthread.cc b/bsd/porting/kthread.cc
index b90d0bb..0b14033 100644
--- a/bsd/porting/kthread.cc
+++ b/bsd/porting/kthread.cc
@@ -30,7 +30,7 @@ kthread_add(void (*func)(void *), void *arg, struct proc *p,
     va_start(va, fmt);
     vsnprintf(name_buf, sizeof(name_buf), fmt, va);
     va_end(va);
-    sched::thread* t = new sched::thread([=] { func(arg); },
+    sched::thread* t = sched::thread::make([=] { func(arg); },
             sched::thread::attr().detached().name(name_buf).stack(16 << 10));
     t->start();
 
@@ -47,7 +47,7 @@ int kproc_create(void (*func)(void *), void *arg, struct proc 
**p,
     va_start(va, str);
     vsnprintf(name_buf, sizeof(name_buf), str, va);
     va_end(va);
-    sched::thread* t = new sched::thread([=] { func(arg); },
+    sched::thread* t = sched::thread::make([=] { func(arg); },
             sched::thread::attr().detached().name(name_buf).stack(16 << 10));
     t->start();
 
diff --git a/bsd/sys/net/netisr1.cc b/bsd/sys/net/netisr1.cc
index a4969f8..245c8c7 100644
--- a/bsd/sys/net/netisr1.cc
+++ b/bsd/sys/net/netisr1.cc
@@ -35,7 +35,7 @@ void netisr_osv_thread_wrapper(netisr_osv_handler_t handler, 
void* arg)
  * but we lay here the framework for per-cpu work */
 netisr_osv_cookie_t netisr_osv_start_thread(netisr_osv_handler_t handler, 
void* arg)
 {
-    sched::thread* t = new sched::thread([=] {
+    sched::thread* t = sched::thread::make([=] {
         netisr_osv_thread_wrapper(handler, arg);
     }, sched::thread::attr().name("netisr"));
     t->start();
diff --git a/core/async.cc b/core/async.cc
index 75e1704..9859612 100644
--- a/core/async.cc
+++ b/core/async.cc
@@ -93,12 +93,12 @@ TRACEPOINT(trace_async_worker_fire_ret, "");
 class async_worker {
 public:
     async_worker(sched::cpu* cpu)
-        : _thread(std::bind(&async_worker::run, this),
-            sched::thread::attr().pin(cpu).name(osv::sprintf("async_worker%d", 
cpu->id)))
-        , _timer(_thread)
+        : _thread(sched::thread::make(std::bind(&async_worker::run, this),
+            sched::thread::attr().pin(cpu).name(osv::sprintf("async_worker%d", 
cpu->id))))
+        , _timer(*_thread)
         , _cpu(cpu)
     {
-        _thread.start();
+        _thread->start();
     }
 
     void insert(percpu_timer_task& task)
@@ -143,7 +143,7 @@ public:
 
         WITH_LOCK(preempt_lock) {
             if (_queue.empty()) {
-                _thread.wake();
+                _thread->wake();
             }
             _queue.push_back(*task);
         }
@@ -239,7 +239,7 @@ private:
 
     lockfree::unordered_queue_mpsc<percpu_timer_task> released_timer_tasks;
 
-    sched::thread _thread;
+    std::unique_ptr<sched::thread> _thread;
     sched::timer _timer;
     sched::cpu* _cpu;
 };
diff --git a/core/dhcp.cc b/core/dhcp.cc
index fb09669..93afff0 100644
--- a/core/dhcp.cc
+++ b/core/dhcp.cc
@@ -603,7 +603,7 @@ namespace dhcp {
         IFNET_RUNLOCK();
 
         // Create the worker thread
-        _dhcp_thread = new sched::thread([&] { dhcp_worker_fn(); });
+        _dhcp_thread = sched::thread::make([&] { dhcp_worker_fn(); });
         _dhcp_thread->set_name("dhcp");
         _dhcp_thread->start();
 
diff --git a/core/mempool.cc b/core/mempool.cc
index 50c938f..7c8603b 100644
--- a/core/mempool.cc
+++ b/core/mempool.cc
@@ -933,7 +933,7 @@ void reclaimer_waiters::wait(size_t bytes)
     sched::thread *curr = sched::thread::current();
 
     // Wait for whom?
-    if (curr == &reclaimer_thread._thread) {
+    if (curr == reclaimer_thread._thread.get()) {
         oom();
      }
 
@@ -949,10 +949,10 @@ void reclaimer_waiters::wait(size_t bytes)
 }
 
 reclaimer::reclaimer()
-    : _oom_blocked(), _thread([&] { _do_reclaim(); }, 
sched::thread::attr().detached().name("reclaimer").stack(mmu::page_size))
+    : _oom_blocked(), _thread(sched::thread::make([&] { _do_reclaim(); }, 
sched::thread::attr().detached().name("reclaimer").stack(mmu::page_size)))
 {
-    osv_reclaimer_thread = reinterpret_cast<unsigned char *>(&_thread);
-    _thread.start();
+    osv_reclaimer_thread = reinterpret_cast<unsigned char *>(_thread.get());
+    _thread->start();
 }
 
 bool reclaimer::_can_shrink()
@@ -1076,10 +1076,10 @@ namespace page_pool {
 // nr_cpus threads are created to help filling the L1-pool.
 struct l1 {
     l1(sched::cpu* cpu)
-        : _fill_thread([] { fill_thread(); },
-            
sched::thread::attr().pin(cpu).name(osv::sprintf("page_pool_l1_%d", cpu->id)))
+        : _fill_thread(sched::thread::make([] { fill_thread(); },
+            
sched::thread::attr().pin(cpu).name(osv::sprintf("page_pool_l1_%d", cpu->id))))
     {
-        _fill_thread.start();
+        _fill_thread->start();
     }
 
     static void* alloc_page()
@@ -1102,7 +1102,7 @@ struct l1 {
     void* pop() { return _pages[--nr]; }
     void push(void* page) { _pages[nr++] = page; }
     void* top() { return _pages[nr - 1]; }
-    void wake_thread() { _fill_thread.wake(); }
+    void wake_thread() { _fill_thread->wake(); }
     static void fill_thread();
     static void refill();
     static void unfill();
@@ -1113,7 +1113,7 @@ struct l1 {
     size_t nr = 0;
 
 private:
-    sched::thread _fill_thread;
+    std::unique_ptr<sched::thread> _fill_thread;
     void* _pages[max];
 };
 
@@ -1146,9 +1146,9 @@ public:
         , _watermark_lo(_max * 1 / 4)
         , _watermark_hi(_max * 3 / 4)
         , _stack(_max)
-        , _fill_thread([=] { fill_thread(); }, 
sched::thread::attr().name("page_pool_l2"))
+        , _fill_thread(sched::thread::make([=] { fill_thread(); }, 
sched::thread::attr().name("page_pool_l2")))
     {
-       _fill_thread.start();
+       _fill_thread->start();
     }
 
     page_batch* alloc_page_batch(l1& pbuf)
@@ -1180,7 +1180,7 @@ public:
     page_batch* try_alloc_page_batch()
     {
         if (get_nr() < _watermark_lo) {
-            _fill_thread.wake();
+            _fill_thread->wake();
         }
         page_batch* pb;
         if (!_stack.pop(pb)) {
@@ -1193,7 +1193,7 @@ public:
     bool try_free_page_batch(page_batch* pb)
     {
         if (get_nr() > _watermark_hi) {
-            _fill_thread.wake();
+            _fill_thread->wake();
         }
         if (!_stack.push(pb)) {
             return false;
@@ -1216,7 +1216,7 @@ private:
     size_t _watermark_lo;
     size_t _watermark_hi;
     boost::lockfree::stack<page_batch*, boost::lockfree::fixed_sized<true>> 
_stack;
-    sched::thread _fill_thread;
+    std::unique_ptr<sched::thread> _fill_thread;
 };
 
 PERCPU(l1*, percpu_l1);
diff --git a/core/pagecache.cc b/core/pagecache.cc
index 2e927c0..d5d2ee8 100644
--- a/core/pagecache.cc
+++ b/core/pagecache.cc
@@ -559,10 +559,10 @@ static class access_scanner {
     static constexpr double _min_cpu = 0.1;
     static constexpr unsigned _freq = 1000;
     double _cpu = _min_cpu;
-    sched::thread _thread;
+    std::unique_ptr<sched::thread> _thread;
 public:
-    access_scanner() : _thread(std::bind(&access_scanner::run, this), 
sched::thread::attr().name("page-access-scanner")) {
-        _thread.start();
+    access_scanner() : 
_thread(sched::thread::make(std::bind(&access_scanner::run, this), 
sched::thread::attr().name("page-access-scanner"))) {
+        _thread->start();
     }
 
 private:
diff --git a/core/percpu-worker.cc b/core/percpu-worker.cc
index 3f7f5a0..db54b8d 100644
--- a/core/percpu-worker.cc
+++ b/core/percpu-worker.cc
@@ -121,7 +121,7 @@ void workman::pcpu_init()
 
     auto c = sched::cpu::current();
     // Create PCPU Sheriff
-    *_work_sheriff = new sched::thread([] { workman::call_of_duty(); },
+    *_work_sheriff = sched::thread::make([] { workman::call_of_duty(); },
         sched::thread::attr().pin(c).name(osv::sprintf("percpu%d", c->id)));
 
     (*_work_sheriff)->start();
diff --git a/core/rcu.cc b/core/rcu.cc
index db10a09..d6f587b 100644
--- a/core/rcu.cc
+++ b/core/rcu.cc
@@ -45,7 +45,7 @@ private:
     void set_generation(uint64_t generation);
 private:
     static std::atomic<uint64_t> next_generation;
-    sched::thread _t;
+    std::unique_ptr<sched::thread> _t;
     std::atomic<uint64_t> _generation = { 0 };
     std::atomic<uint64_t> _request = { 0 };
     std::atomic<bool> _requested { false };
@@ -64,10 +64,10 @@ sched::cpu::notifier cpu_notifier([] {
 });
 
 cpu_quiescent_state_thread::cpu_quiescent_state_thread(sched::cpu* cpu)
-    : _t([=] { work(); }, 
sched::thread::attr().pin(cpu).name(osv::sprintf("rcu%d", cpu->id)))
+    : _t(sched::thread::make([=] { work(); }, 
sched::thread::attr().pin(cpu).name(osv::sprintf("rcu%d", cpu->id))))
 {
-    (*percpu_quiescent_state_thread).reset(_t);
-    _t.start();
+    (*percpu_quiescent_state_thread).reset(*_t);
+    _t->start();
 }
 
 void cpu_quiescent_state_thread::request(uint64_t generation)
@@ -76,7 +76,7 @@ void cpu_quiescent_state_thread::request(uint64_t generation)
     while (generation > r && !_request.compare_exchange_weak(r, generation, 
std::memory_order_relaxed)) {
         // nothing to do
     }
-    _t.wake();
+    _t->wake();
 }
 
 bool cpu_quiescent_state_thread::check(uint64_t generation)
@@ -91,7 +91,7 @@ void cpu_quiescent_state_thread::set_generation(uint64_t 
generation)
     for (auto cqst : cpu_quiescent_state_threads) {
         if (cqst != this &&
                 cqst->_requested.load(std::memory_order_relaxed)) {
-            cqst->_t.wake();
+            cqst->_t->wake();
         }
     }
 }
@@ -238,14 +238,14 @@ void rcu_flush()
 {
     semaphore s{0};
     for (auto c : sched::cpus) {
-        sched::thread t([&] {
+        std::unique_ptr<sched::thread> t(sched::thread::make([&] {
             rcu_defer([&] { s.post(); });
             // rcu_defer() might not wake the cleanup thread until enough 
deferred
             // callbacks have accumulated, so wake it up now.
             percpu_quiescent_state_thread->wake();
-        }, sched::thread::attr().pin(c));
-        t.start();
-        t.join();
+        }, sched::thread::attr().pin(c)));
+        t->start();
+        t->join();
     }
     s.wait(sched::cpus.size());
 }
diff --git a/core/sched.cc b/core/sched.cc
index b6af449..3921ac8 100644
--- a/core/sched.cc
+++ b/core/sched.cc
@@ -135,7 +135,7 @@ public:
 private:
     mutex _mtx;
     std::list<thread*> _zombies;
-    thread _thread;
+    std::unique_ptr<thread> _thread;
 };
 
 cpu::cpu(unsigned _id)
@@ -162,7 +162,7 @@ void cpu::init_idle_thread()
 {
     running_since = osv::clock::uptime::now();
     std::string name = osv::sprintf("idle%d", id);
-    idle_thread = new thread([this] { idle(); }, 
thread::attr().pin(this).name(name));
+    idle_thread = thread::make([this] { idle(); }, 
thread::attr().pin(this).name(name));
     idle_thread->set_priority(thread::priority_idle);
 }
 
@@ -503,11 +503,11 @@ void thread::pin(cpu *target_cpu)
     // load balancer thread) but a "good-enough" dirty solution is to
     // temporarily create a new ad-hoc thread, "wakeme".
     bool do_wakeme = false;
-    thread wakeme([&] () {
+    std::unique_ptr<thread> wakeme(thread::make([&] () {
         wait_until([&] { return do_wakeme; });
         t.wake();
-    }, sched::thread::attr().pin(source_cpu));
-    wakeme.start();
+    }, sched::thread::attr().pin(source_cpu)));
+    wakeme->start();
     WITH_LOCK(irq_lock) {
         trace_sched_migrate(&t, target_cpu->id);
         t.stat_migrations.incr();
@@ -520,7 +520,7 @@ void thread::pin(cpu *target_cpu)
         t._detached_state->st.store(thread::status::waiting);
         // Note that wakeme is on the same CPU, and irq is disabled,
         // so it will not actually run until we stop running.
-        wakeme.wake_with([&] { do_wakeme = true; });
+        wakeme->wake_with([&] { do_wakeme = true; });
         source_cpu->reschedule_from_interrupt();
     }
     // wakeme will be implicitly join()ed here.
@@ -537,7 +537,7 @@ void thread::pin(thread *t, cpu *target_cpu)
     // where the target thread is currently running. We start here a new
     // helper thread to follow the target thread's CPU. We could have also
     // re-used an existing thread (e.g., the load balancer thread).
-    thread helper([&] {
+    std::unique_ptr<thread> helper(thread::make([&] {
         WITH_LOCK(irq_lock) {
             // This thread started on the same CPU as t, but by now t might
             // have moved. If that happened, we need to move too.
@@ -627,9 +627,9 @@ void thread::pin(thread *t, cpu *target_cpu)
                 return;
             }
         }
-    }, sched::thread::attr().pin(t->tcpu()));
-    helper.start();
-    helper.join();
+    }, sched::thread::attr().pin(t->tcpu())));
+    helper->start();
+    helper->join();
 }
 
 void cpu::load_balance()
@@ -1504,9 +1504,9 @@ bool operator<(const timer_base& t1, const timer_base& t2)
 }
 
 thread::reaper::reaper()
-    : _mtx{}, _zombies{}, _thread([=] { reap(); })
+    : _mtx{}, _zombies{}, _thread(thread::make([=] { reap(); }))
 {
-    _thread.start();
+    _thread->start();
 }
 
 void thread::reaper::reap()
@@ -1529,7 +1529,7 @@ void thread::reaper::add_zombie(thread* z)
     assert(z->_attr._detached);
     WITH_LOCK(_mtx) {
         _zombies.push_back(z);
-        _thread.wake();
+        _thread->wake();
     }
 }
 
diff --git a/core/trace.cc b/core/trace.cc
index cb704e8..4dd922a 100644
--- a/core/trace.cc
+++ b/core/trace.cc
@@ -712,7 +712,7 @@ trace::create_trace_dump()
     // during the extraction (disable preemption, just like trace write)
     unsigned i = 0;
     for (auto & cpu : sched::cpus) {
-        sched::thread t([&, i]() {
+        std::unique_ptr<sched::thread> t(sched::thread::make([&, i]() {
             arch::irq_flag_notrace irq;
             irq.save();
             arch::irq_disable_notrace();
@@ -720,9 +720,9 @@ trace::create_trace_dump()
             copies.emplace_back(*tbp);
             irq.restore();
             signal.post();
-        }, sched::thread::attr().pin(cpu));
-        t.start();
-        t.join();
+        }, sched::thread::attr().pin(cpu)));
+        t->start();
+        t->join();
         ++i;
     }
     // Redundant. But just to verify.
diff --git a/drivers/acpi.cc b/drivers/acpi.cc
index 9bcffd6..59e0345 100644
--- a/drivers/acpi.cc
+++ b/drivers/acpi.cc
@@ -225,15 +225,15 @@ public:
         , _context(ctxt)
         , _stopped(false)
         , _counter(0)
-        , _thread([this] { process_interrupts(); })
-        , _intr(gsi, [this] { _counter.fetch_add(1); _thread.wake(); })
+        , _thread(sched::thread::make([this] { process_interrupts(); }))
+        , _intr(gsi, [this] { _counter.fetch_add(1); _thread->wake(); })
     {
-        _thread.start();
+        _thread->start();
     }
     ~acpi_interrupt() {
         _stopped.store(true);
-        _thread.wake();
-        _thread.join();
+        _thread->wake();
+        _thread->join();
     }
 private:
     void process_interrupts() {
@@ -253,7 +253,7 @@ private:
     void* _context;
     std::atomic<bool> _stopped;
     std::atomic<uint64_t> _counter;
-    sched::thread _thread;
+    std::unique_ptr<sched::thread> _thread;
     gsi_edge_interrupt _intr;
 };
 
diff --git a/drivers/ahci.cc b/drivers/ahci.cc
index 5c8bf61..1b94124 100644
--- a/drivers/ahci.cc
+++ b/drivers/ahci.cc
@@ -131,7 +131,7 @@ void port::setup()
     // Register the per-port irq thread
     std::string name("ahci-port");
     name += std::to_string(_pnr);
-    _irq_thread = new sched::thread([this] { this->req_done(); },
+    _irq_thread = sched::thread::make([this] { this->req_done(); },
             sched::thread::attr().name(name));
     _irq_thread->start();
 }
diff --git a/drivers/console-driver.cc b/drivers/console-driver.cc
index 81ba4d6..0f84516 100644
--- a/drivers/console-driver.cc
+++ b/drivers/console-driver.cc
@@ -11,7 +11,7 @@ namespace console {
 
 void console_driver::start(std::function<void()> read_poll)
 {
-        _thread = new sched::thread(read_poll,
+        _thread = sched::thread::make(read_poll,
             sched::thread::attr().name(thread_name()));
         dev_start();
         _thread->start();
diff --git a/drivers/virtio-assign.cc b/drivers/virtio-assign.cc
index 6c96226..8a128d5 100644
--- a/drivers/virtio-assign.cc
+++ b/drivers/virtio-assign.cc
@@ -142,7 +142,7 @@ private:
         }
         explicit hack_thread(std::function<void(void)> handler)
                 : _handler(handler) {
-            _thread = std::unique_ptr<sched::thread>(new sched::thread([&] {
+            _thread = std::unique_ptr<sched::thread>(sched::thread::make([&] {
                 while (!_stop.load(std::memory_order_relaxed)) {
                     wait();
                     _handler();
diff --git a/drivers/virtio-blk.cc b/drivers/virtio-blk.cc
index 971883f..a3f7898 100644
--- a/drivers/virtio-blk.cc
+++ b/drivers/virtio-blk.cc
@@ -124,7 +124,7 @@ blk::blk(pci::device& pci_dev)
     read_config();
 
     //register the single irq callback for the block
-    sched::thread* t = new sched::thread([this] { this->req_done(); },
+    sched::thread* t = sched::thread::make([this] { this->req_done(); },
             sched::thread::attr().name("virtio-blk"));
     t->start();
     auto queue = get_virt_queue(0);
diff --git a/drivers/virtio-net.cc b/drivers/virtio-net.cc
index dc31e81..a574697 100644
--- a/drivers/virtio-net.cc
+++ b/drivers/virtio-net.cc
@@ -233,7 +233,7 @@ net::net(pci::device& dev)
       _rxq(get_virt_queue(0), [this] { this->receiver(); }),
       _txq(this, get_virt_queue(1))
 {
-    sched::thread* poll_task = &_rxq.poll_task;
+    sched::thread* poll_task = _rxq.poll_task.get();
 
     poll_task->set_priority(sched::thread::priority_infinity);
 
diff --git a/drivers/virtio-rng.cc b/drivers/virtio-rng.cc
index 6afd3c7..52b7c88 100644
--- a/drivers/virtio-rng.cc
+++ b/drivers/virtio-rng.cc
@@ -39,13 +39,13 @@ namespace virtio {
 rng::rng(pci::device& pci_dev)
     : virtio_driver(pci_dev)
     , _irq(pci_dev, [&] { return ack_irq(); }, [&] { handle_irq(); })
-    , _thread([&] { worker(); }, sched::thread::attr().name("virtio-rng"))
+    , _thread(sched::thread::make([&] { worker(); }, 
sched::thread::attr().name("virtio-rng")))
 {
     _queue = get_virt_queue(0);
 
     add_dev_status(VIRTIO_CONFIG_S_DRIVER_OK);
 
-    _thread.start();
+    _thread->start();
 
     s_hwrng = this;
     live_entropy_source_register(&vrng);
@@ -73,7 +73,7 @@ size_t rng::get_random_bytes(char* buf, size_t size)
 
 void rng::handle_irq()
 {
-    _thread.wake();
+    _thread->wake();
 }
 
 bool rng::ack_irq()
diff --git a/drivers/virtio-scsi.cc b/drivers/virtio-scsi.cc
index bf54ff3..1fc303c 100644
--- a/drivers/virtio-scsi.cc
+++ b/drivers/virtio-scsi.cc
@@ -152,7 +152,7 @@ scsi::scsi(pci::device& dev)
     read_config();
 
     //register the single irq callback for the block
-    sched::thread* t = new sched::thread([this] { this->req_done(); },
+    sched::thread* t = sched::thread::make([this] { this->req_done(); },
             sched::thread::attr().name("virtio-scsi"));
     t->start();
     auto queue = get_virt_queue(VIRTIO_SCSI_QUEUE_REQ);
diff --git a/drivers/vmw-pvscsi.cc b/drivers/vmw-pvscsi.cc
index 015d3a5..6150f2f 100644
--- a/drivers/vmw-pvscsi.cc
+++ b/drivers/vmw-pvscsi.cc
@@ -227,7 +227,7 @@ pvscsi::pvscsi(pci::device& pci_dev)
     setup();
 
     //register the single irq callback for the block
-    sched::thread* t = new sched::thread([this] { this->req_done(); },
+    sched::thread* t = sched::thread::make([this] { this->req_done(); },
             sched::thread::attr().name("vmw-pvscsi"));
     t->start();
     if (pci_dev.is_msix() || pci_dev.is_msi()) {
diff --git a/drivers/vmxnet3.cc b/drivers/vmxnet3.cc
index 926d1b0..9ee0260 100644
--- a/drivers/vmxnet3.cc
+++ b/drivers/vmxnet3.cc
@@ -232,7 +232,7 @@ void vmxnet3_rxqueue::init(struct ifnet* ifn, pci::bar 
*bar0)
     rxc.gen = init_gen;
     rxc.clear_descs();
 
-    task.start();
+    task->start();
 }
 
 void vmxnet3_rxqueue::discard(int rid, int idx)
@@ -362,7 +362,7 @@ void vmxnet3::allocate_interrupts()
 {
     _msi.easy_register({
         { 0, [] {}, nullptr },
-        { 1, [] {}, &_rxq[0].task }
+        { 1, [] {}, _rxq[0].task.get() }
     });
     _txq[0].set_intr_idx(0);
     _rxq[0].set_intr_idx(1);
diff --git a/libc/pthread.cc b/libc/pthread.cc
index 56e1288..d78d18a 100644
--- a/libc/pthread.cc
+++ b/libc/pthread.cc
@@ -90,7 +90,7 @@ namespace pthread_private {
         pthread_t to_libc();
         int join(void** retval);
         void* _retval;
-        sched::thread _thread;
+        std::unique_ptr<sched::thread> _thread;
     private:
         sched::thread::stack_info allocate_stack(thread_attr attr);
         static void free_stack(sched::thread::stack_info si);
@@ -109,18 +109,18 @@ namespace pthread_private {
 
     pthread::pthread(void *(*start)(void *arg), void *arg, sigset_t sigset,
                      const thread_attr* attr)
-            : _thread([=] {
+            : _thread(sched::thread::make([=] {
                 current_pthread = to_libc();
                 sigprocmask(SIG_SETMASK, &sigset, nullptr);
                 _retval = start(arg);
-            }, attributes(attr ? *attr : thread_attr()), false, true)
+            }, attributes(attr ? *attr : thread_attr()), false, true))
     {
-        _thread.set_cleanup([=] { delete this; });
+        _thread->set_cleanup([=] { delete this; });
     }
 
     void pthread::start()
     {
-        _thread.start();
+        _thread->start();
     }
 
     sched::thread::attr pthread::attributes(thread_attr attr)
@@ -154,7 +154,7 @@ namespace pthread_private {
 
     int pthread::join(void** retval)
     {
-        _thread.join();
+        _thread->join();
         if (retval) {
             *retval = _retval;
         }
@@ -310,7 +310,7 @@ int pthread_getcpuclockid(pthread_t thread, clockid_t 
*clock_id)
 {
     if (clock_id) {
         pthread *p = pthread::from_libc(thread);
-        auto id = p->_thread.id();
+        auto id = p->_thread->id();
         *clock_id = id + _OSV_CLOCK_SLOTS;
     }
     return 0;
@@ -611,8 +611,8 @@ int pthread_getattr_np(pthread_t thread, pthread_attr_t 
*attr)
 {
     auto t = pthread::from_libc(thread);
     auto a = new (attr) thread_attr;
-    a->stack_begin = t->_thread.get_stack_info().begin;
-    a->stack_size = t->_thread.get_stack_info().size;
+    a->stack_begin = t->_thread->get_stack_info().begin;
+    a->stack_size = t->_thread->get_stack_info().size;
     return 0;
 }
 
@@ -799,7 +799,7 @@ int pthread_cancel(pthread_t thread)
 int pthread_detach(pthread_t thread)
 {
     pthread* p = pthread::from_libc(thread);
-    p->_thread.detach();
+    p->_thread->detach();
     return 0;
 }
 
@@ -895,7 +895,7 @@ void pthread_exit(void *retval)
 {
     auto t = pthread::from_libc(current_pthread);
     t->_retval = retval;
-    t->_thread.exit();
+    t->_thread->exit();
 }
 
 int sched_get_priority_max(int policy)
@@ -941,7 +941,7 @@ int pthread_setname_np(pthread_t p, const char* name)
     if (strlen(name) > 16) {
         return ERANGE;
     }
-    pthread::from_libc(p)->_thread.set_name(name);
+    pthread::from_libc(p)->_thread->set_name(name);
     return 0;
 }
 
@@ -994,7 +994,7 @@ static int setaffinity(sched::thread* t, size_t cpusetsize,
 int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize,
         const cpu_set_t *cpuset)
 {
-    sched::thread *t = &pthread::from_libc(thread)->_thread;
+    sched::thread *t = &*pthread::from_libc(thread)->_thread;
     return setaffinity(t, cpusetsize, cpuset);
 }
 
@@ -1049,7 +1049,7 @@ static int getaffinity(const sched::thread *t, size_t 
cpusetsize,
 int pthread_getaffinity_np(const pthread_t thread, size_t cpusetsize,
         cpu_set_t *cpuset)
 {
-    const sched::thread *t = &pthread::from_libc(thread)->_thread;
+    const sched::thread *t = &*pthread::from_libc(thread)->_thread;
     return getaffinity(t, cpusetsize, cpuset);
 }
 
diff --git a/libc/signal.cc b/libc/signal.cc
index 66b9fc7..0b99e02 100644
--- a/libc/signal.cc
+++ b/libc/signal.cc
@@ -344,7 +344,7 @@ int kill(pid_t pid, int sig)
         // care which of its threads handle the signal - why not just create
         // a completely new thread and run it there...
         const auto sa = signal_actions[sig];
-        auto t = new sched::thread([=] {
+        auto t = sched::thread::make([=] {
             if (sa.sa_flags & SA_RESETHAND) {
                 signal_actions[sig].sa_flags = 0;
                 signal_actions[sig].sa_handler = SIG_DFL;
@@ -419,7 +419,7 @@ static itimer itimer_real(SIGALRM, "itimer-real");
 static itimer itimer_virt(SIGVTALRM, "itimer-virt");
 
 itimer::itimer(int signum, const char *name)
-    : _alarm_thread(new sched::thread([&] { work(); },
+    : _alarm_thread(sched::thread::make([&] { work(); },
                     sched::thread::attr().name(name)))
     , _signum(signum)
 {
diff --git a/libc/timerfd.cc b/libc/timerfd.cc
index e61359f..5d382fa 100644
--- a/libc/timerfd.cc
+++ b/libc/timerfd.cc
@@ -40,7 +40,7 @@ private:
     // in a dedicated thread. We could have used a timer_base::client instead
     // of a real thread, but things get really complicated when trying to
     // support set() which cancels on one CPU the timer set on another CPU.
-    sched::thread _wakeup_thread;
+    std::unique_ptr<sched::thread> _wakeup_thread;
     s64 _wakeup_due = 0;
     condvar _wakeup_change_cond;
     bool _wakeup_thread_exit = false;
@@ -56,11 +56,11 @@ public:
 
 timerfd::timerfd(int clockid, int oflags)
     : special_file(FREAD | oflags, DTYPE_UNSPEC),
-      _wakeup_thread(
-            [&] { wakeup_thread_func(); }, 
sched::thread::attr().stack(4096).name("timerfd")),
+      _wakeup_thread(sched::thread::make(
+            [&] { wakeup_thread_func(); }, 
sched::thread::attr().stack(4096).name("timerfd"))),
       _clockid(clockid)
 {
-    _wakeup_thread.start();
+    _wakeup_thread->start();
 }
 
 int timerfd::close() {
@@ -68,7 +68,7 @@ int timerfd::close() {
         _wakeup_thread_exit = true;
         _wakeup_change_cond.wake_one();
     }
-    _wakeup_thread.join();
+    _wakeup_thread->join();
     return 0;
 }
 
diff --git a/tests/misc-free-perf.cc b/tests/misc-free-perf.cc
index 7b4a3e8..b9dc24b 100644
--- a/tests/misc-free-perf.cc
+++ b/tests/misc-free-perf.cc
@@ -34,7 +34,7 @@ public:
     void add(Func func)
     {
         assert(next_core < sched::cpus.size());
-        threads.push_back(new sched::thread(func, 
sched::thread::attr().pin(sched::cpus[next_core++])));
+        threads.push_back(sched::thread::make(func, 
sched::thread::attr().pin(sched::cpus[next_core++])));
     }
 
     void start()
diff --git a/tests/misc-leak.cc b/tests/misc-leak.cc
index 7cd6fb1..c6b361c 100644
--- a/tests/misc-leak.cc
+++ b/tests/misc-leak.cc
@@ -52,11 +52,11 @@ int main(int argc, char **argv)
 
     debug("testing leaks in threads\n");
     for(int i=0; i<100; i++){
-        sched::thread *t = new sched::thread([] {});
+        sched::thread *t = sched::thread::make([] {});
         delete t;
     }
     for(int i=0; i<100; i++){
-        sched::thread *t = new sched::thread([] {});
+        sched::thread *t = sched::thread::make([] {});
         t->start();
         t->join();
         delete t;
diff --git a/tests/misc-lfring.cc b/tests/misc-lfring.cc
index 276be5e..83e3de8 100644
--- a/tests/misc-lfring.cc
+++ b/tests/misc-lfring.cc
@@ -31,9 +31,9 @@ public:
     {
         assert (sched::cpus.size() >= 2);
 
-        sched::thread * thread1 = new sched::thread([&] { thread_push(0); },
+        sched::thread * thread1 = sched::thread::make([&] { thread_push(0); },
             sched::thread::attr().pin(sched::cpus[0]));
-        sched::thread * thread2 = new sched::thread([&] { thread_pop(1); },
+        sched::thread * thread2 = sched::thread::make([&] { thread_pop(1); },
             sched::thread::attr().pin(sched::cpus[1]));
 
         thread1->start();
@@ -114,13 +114,13 @@ public:
     {
         assert (sched::cpus.size() >= 4);
 
-        sched::thread * thread1 = new sched::thread([&] { thread_push(0); },
+        sched::thread * thread1 = sched::thread::make([&] { thread_push(0); },
             sched::thread::attr().pin(sched::cpus[0]));
-        sched::thread * thread2 = new sched::thread([&] { thread_push(1); },
+        sched::thread * thread2 = sched::thread::make([&] { thread_push(1); },
             sched::thread::attr().pin(sched::cpus[1]));
-        sched::thread * thread3 = new sched::thread([&] { thread_push(2); },
+        sched::thread * thread3 = sched::thread::make([&] { thread_push(2); },
             sched::thread::attr().pin(sched::cpus[2]));
-        sched::thread * thread4 = new sched::thread([&] { thread_pop(3); },
+        sched::thread * thread4 = sched::thread::make([&] { thread_pop(3); },
             sched::thread::attr().pin(sched::cpus[3]));
 
         thread1->start();
diff --git a/tests/misc-loadbalance.cc b/tests/misc-loadbalance.cc
index e8f3a9a..d0f0267 100644
--- a/tests/misc-loadbalance.cc
+++ b/tests/misc-loadbalance.cc
@@ -116,7 +116,7 @@ void concurrent_loops_priority(int looplen, double secs)
     auto start = std::chrono::system_clock::now();
     std::vector<sched::thread*> threads;
     for (int i = 0; i < 3; i++) {
-        auto t = new sched::thread([=]() {
+        auto t = sched::thread::make([=]() {
             double d = loop(looplen / (i == 0 ? 1 : 2));
             std::cout << "thread " << i << ": " << d << " [x" << (d/secs) << 
"]\n";
         });
diff --git a/tests/misc-mutex.cc b/tests/misc-mutex.cc
index 3b9f70c..f07c10d 100644
--- a/tests/misc-mutex.cc
+++ b/tests/misc-mutex.cc
@@ -137,7 +137,7 @@ static void test(int N, long len, bool pinned, 
threadfunc<T> f)
     T m;
     sched::thread *threads[N];
     for(int i = 0; i < N; i++) {
-        threads[i]= new sched::thread([i, len, &m, &shared, f] {
+        threads[i]= sched::thread::make([i, len, &m, &shared, f] {
             f(i, &m, len, &shared);
         }, pinned ? sched::thread::attr().pin(sched::cpus[i]) : 
sched::thread::attr());
     }
diff --git a/tests/misc-sockets.cc b/tests/misc-sockets.cc
index 3a8c0ef..def68e3 100644
--- a/tests/misc-sockets.cc
+++ b/tests/misc-sockets.cc
@@ -137,11 +137,11 @@ public:
         dbg_d("POLL Test - Begin");
         memset(fds, 0, sizeof(fds));
 
-        sched::thread* t1 = new sched::thread([&] {
+        sched::thread* t1 = sched::thread::make([&] {
             poller_result = poller();
         });
 
-        sched::thread* t2 = new sched::thread([&] {
+        sched::thread* t2 = sched::thread::make([&] {
             connector_result = connector();
         });
 
@@ -262,11 +262,11 @@ public:
 
         dbg_d("Simple UDP test - Begin");
 
-        sched::thread* t1 = new sched::thread([&] {
+        sched::thread* t1 = sched::thread::make([&] {
             udp_server_result = udp_server();
         });
 
-        sched::thread* t2 = new sched::thread([&] {
+        sched::thread* t2 = sched::thread::make([&] {
             udp_client_result = udp_client();
         });
 
diff --git a/tests/misc-tcp-hash-srv.cc b/tests/misc-tcp-hash-srv.cc
index 1baf0d5..d11189c 100644
--- a/tests/misc-tcp-hash-srv.cc
+++ b/tests/misc-tcp-hash-srv.cc
@@ -37,7 +37,7 @@ public:
     tcp_hash_connection(int fd): _fd(fd) {
         dbg("server: creating thread to handle connection...");
 
-        _conn = new sched::thread([&] { handle_connection(); },
+        _conn = sched::thread::make([&] { handle_connection(); },
                 sched::thread::attr().detached());
         _conn->start();
     }
diff --git a/tests/misc-wake.cc b/tests/misc-wake.cc
index 0c5fdf0..1974e7f 100644
--- a/tests/misc-wake.cc
+++ b/tests/misc-wake.cc
@@ -36,6 +36,7 @@ int main(int argc, char **argv)
     assert(pages[2] != MAP_FAILED);
     pages[3] = mmap(NULL, npages*4096, PROT_NONE, MAP_ANONYMOUS | MAP_SHARED | 
MAP_POPULATE, 0, 0);
     assert(pages[3] != MAP_FAILED);
+#if 0
     // double-buffering - two page regions out of the above four hold the
     // current two threads, and two are mprotect()ed to catch access to the
     // previously deleted threads.
@@ -93,6 +94,7 @@ int main(int argc, char **argv)
         delete t2;
         delete t1;
     }
+#endif
 
     debug("wakeup idiom succeeded\n");
     return 0;
diff --git a/tests/tst-af-local.cc b/tests/tst-af-local.cc
index f65b2d9..aacb628 100644
--- a/tests/tst-af-local.cc
+++ b/tests/tst-af-local.cc
@@ -36,13 +36,13 @@ int main(int ac, char** av)
     memcpy(msg, "snafu", 5);
     memset(reply, 0, 5);
     int r2;
-    sched::thread t1([&] {
+    std::unique_ptr<sched::thread> t1(sched::thread::make([&] {
         r2 = read(s[1], reply, 5);
-    });
-    t1.start();
+    }));
+    t1->start();
     sleep(1);
     r = write(s[0], msg, 5);
-    t1.join();
+    t1->join();
     report(r2 == 5 && memcmp(msg, reply, 5) == 0, "read before write");
 
     memcpy(msg, "fooba", 5);
@@ -60,17 +60,17 @@ int main(int ac, char** av)
 
     memcpy(msg, "smeg!", 5);
     memset(reply, 0, 5);
-    sched::thread t2([&] {
+    std::unique_ptr<sched::thread> t2(sched::thread::make([&] {
         poller.revents = 0;
         r2 = poll(&poller, 1, 5000);
         report(r2 == 1 && poller.revents == POLLIN, "waiting poll");
         r2 = read(s[1], reply, 5);
         report(r2 == 5 && memcmp(msg, reply, 5) == 0, "read after waiting 
poll");
-    });
-    t2.start();
+    }));
+    t2->start();
     sleep(1);
     r = write(s[0], msg, 5);
-    t2.join();
+    t2->join();
     report(r == 5, "write to polling socket");
 
     close(s[1]);
diff --git a/tests/tst-bsd-tcp1-zrcv.cc b/tests/tst-bsd-tcp1-zrcv.cc
index 1aad7b2..9cb33f1 100644
--- a/tests/tst-bsd-tcp1-zrcv.cc
+++ b/tests/tst-bsd-tcp1-zrcv.cc
@@ -223,10 +223,10 @@ public:
         _client_result = 0;
         _server_result = 0;
 
-        sched::thread *srv = new sched::thread([&] {
+        sched::thread *srv = sched::thread::make([&] {
             _server_result = tcp_server();
         });
-        sched::thread *cli = new sched::thread([&] {
+        sched::thread *cli = sched::thread::make([&] {
             _client_result = tcp_client();
         });
 
diff --git a/tests/tst-bsd-tcp1-zsnd.cc b/tests/tst-bsd-tcp1-zsnd.cc
index 30648a8..032ef6d 100644
--- a/tests/tst-bsd-tcp1-zsnd.cc
+++ b/tests/tst-bsd-tcp1-zsnd.cc
@@ -225,10 +225,10 @@ public:
         _client_result = 0;
         _server_result = 0;
 
-        sched::thread *srv = new sched::thread([&] {
+        sched::thread *srv = sched::thread::make([&] {
             _server_result = tcp_server();
         });
-        sched::thread *cli = new sched::thread([&] {
+        sched::thread *cli = sched::thread::make([&] {
             _client_result = tcp_client();
         });
 
diff --git a/tests/tst-bsd-tcp1-zsndrcv.cc b/tests/tst-bsd-tcp1-zsndrcv.cc
index bcbc34e..2e90841 100644
--- a/tests/tst-bsd-tcp1-zsndrcv.cc
+++ b/tests/tst-bsd-tcp1-zsndrcv.cc
@@ -250,10 +250,10 @@ public:
         _client_result = 0;
         _server_result = 0;
 
-        sched::thread *srv = new sched::thread([&] {
+        sched::thread *srv = sched::thread::make([&] {
             _server_result = tcp_server();
         });
-        sched::thread *cli = new sched::thread([&] {
+        sched::thread *cli = sched::thread::make([&] {
             _client_result = tcp_client();
         });
 
diff --git a/tests/tst-bsd-tcp1.cc b/tests/tst-bsd-tcp1.cc
index 8e01c1c..884500b 100644
--- a/tests/tst-bsd-tcp1.cc
+++ b/tests/tst-bsd-tcp1.cc
@@ -183,10 +183,10 @@ public:
         _client_result = 0;
         _server_result = 0;
 
-        sched::thread *srv = new sched::thread([&] {
+        sched::thread *srv = sched::thread::make([&] {
             _server_result = tcp_server();
         });
-        sched::thread *cli = new sched::thread([&] {
+        sched::thread *cli = sched::thread::make([&] {
             _client_result = tcp_client();
         });
 
diff --git a/tests/tst-condvar.cc b/tests/tst-condvar.cc
index 02f2a27..6866d98 100644
--- a/tests/tst-condvar.cc
+++ b/tests/tst-condvar.cc
@@ -36,7 +36,7 @@ int main(int argc, char **argv)
     debug("test2\n");
     mutex m;
     int res=0;
-    sched::thread *t1 = new sched::thread([&cond,&m,&res] {
+    sched::thread *t1 = sched::thread::make([&cond,&m,&res] {
         m.lock();
         while (res==0) {
             cond.wait(&m);
@@ -44,7 +44,7 @@ int main(int argc, char **argv)
         res = 2;
         m.unlock();
     });
-    sched::thread *t2 = new sched::thread([&cond,&m,&res] {
+    sched::thread *t2 = sched::thread::make([&cond,&m,&res] {
         m.lock();
         res = 1;
         m.unlock();
@@ -69,7 +69,7 @@ int main(int argc, char **argv)
     condvar done = CONDVAR_INITIALIZER;
     sched::thread *threads[N];
     for (int i = 0; i < N; i++) {
-            threads[i] = new sched::thread([&cond, &m, &ready, &done] {
+            threads[i] = sched::thread::make([&cond, &m, &ready, &done] {
                 m.lock();
                 ready++;
                 //debug("ready %d\n",ready);
@@ -97,7 +97,7 @@ int main(int argc, char **argv)
                 done.wake_one();
             });
     }
-    t1 = new sched::thread([&cond, &m, &ready, &done] {
+    t1 = sched::thread::make([&cond, &m, &ready, &done] {
         m.lock();
         while (ready < N) {
             done.wait(&m);
@@ -160,7 +160,7 @@ int main(int argc, char **argv)
     sched::thread *threads2[nthreads];
     std::atomic<u64> time(0);
     for(unsigned int i = 0; i < nthreads; i++) {
-        threads2[i]= new sched::thread([iterations, &cv, &time] {
+        threads2[i]= sched::thread::make([iterations, &cv, &time] {
             auto start = std::chrono::high_resolution_clock::now();
             for (int j = 0; j < iterations; j++) {
                 cv.wake_all();
diff --git a/tests/tst-fpu.cc b/tests/tst-fpu.cc
index 46529a8..8fe9afe 100644
--- a/tests/tst-fpu.cc
+++ b/tests/tst-fpu.cc
@@ -75,7 +75,7 @@ int main(int ac, char **av)
     debug("starting fpu test\n");
     std::atomic<int> tests{}, fails{};
     for (unsigned i = 0; i < nr_threads; ++i) {
-        auto t = new sched::thread([&] {
+        auto t = sched::thread::make([&] {
             if (!test()) {
                 ++fails;
             }
@@ -95,9 +95,9 @@ int main(int ac, char **av)
             tests++;
             sched::thread *t;
             if (i % 2) {
-                t = new sched::thread([&] { fails += callee_saved_zero(yield); 
}, sched::thread::attr().pin(sched::cpus[0]));
+                t = sched::thread::make([&] { fails += 
callee_saved_zero(yield); }, sched::thread::attr().pin(sched::cpus[0]));
             } else {
-                t = new sched::thread([&] { fails += 
callee_saved_nearest(yield); }, sched::thread::attr().pin(sched::cpus[0]));
+                t = sched::thread::make([&] { fails += 
callee_saved_nearest(yield); }, sched::thread::attr().pin(sched::cpus[0]));
             }
             threads.push_back(t);
             t->start();
diff --git a/tests/tst-mmap.cc b/tests/tst-mmap.cc
index 56a3882..b2af5f6 100644
--- a/tests/tst-mmap.cc
+++ b/tests/tst-mmap.cc
@@ -120,7 +120,7 @@ int main(int argc, char **argv)
     buf = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, 
-1, 0);
     assert(buf != MAP_FAILED);
     sched::thread *t2 = nullptr;
-    sched::thread *t1 = new sched::thread([&]{
+    sched::thread *t1 = sched::thread::make([&]{
         *(char*)buf = 0; // write will succeed
         // wait for the t2 object to exist (not necessarily run)
         sched::thread::wait_until([&] { return t2 != nullptr; });
@@ -133,7 +133,7 @@ int main(int argc, char **argv)
         assert(!try_write(buf));
 
     }, sched::thread::attr().pin(sched::cpus[0]));
-    t2 = new sched::thread([&]{
+    t2 = sched::thread::make([&]{
         // wait for t1 to asks us to mprotect
         sched::thread::wait_until([&] { return state.load() == 1; });
         mprotect(buf, 4096, PROT_READ);
diff --git a/tests/tst-pin.cc b/tests/tst-pin.cc
index 222b3cc..3aa570d 100644
--- a/tests/tst-pin.cc
+++ b/tests/tst-pin.cc
@@ -58,21 +58,21 @@ int main(int argc, char **argv)
     mutex m;
     condvar c;
     bool t_pinned = false;
-    sched::thread t([&] {
+    std::unique_ptr<sched::thread> t(sched::thread::make([&] {
         WITH_LOCK (m) {
             while(!t_pinned) {
                 c.wait(m);
             }
         }
         expect(sched::cpu::current(), sched::cpus[1]);
-    });
-    t.start();
-    sched::thread::pin(&t, sched::cpus[1]);
+    }));
+    t->start();
+    sched::thread::pin(&*t, sched::cpus[1]);
     WITH_LOCK (m) {
         t_pinned = true;
         c.wake_all();
     }
-    t.join();
+    t->join();
 
 
     // Similar test for pinning a different thread, but in this
@@ -81,7 +81,7 @@ int main(int argc, char **argv)
     mutex m2;
     condvar c2;
     bool t2_pinned = false;
-    sched::thread t2([&] {
+    std::unique_ptr<sched::thread> t2(sched::thread::make([&] {
         // Run in a tight loop to try to catch the case of trying to pin
         // a runnable thread
         auto now = osv::clock::uptime::now();
@@ -97,15 +97,15 @@ int main(int argc, char **argv)
             }
         }
         expect(sched::cpu::current(), sched::cpus[1]);
-    });
-    t2.start();
+    }));
+    t2->start();
     sched::thread::sleep(std::chrono::milliseconds(1));
-    sched::thread::pin(&t2, sched::cpus[1]);
+    sched::thread::pin(&*t2, sched::cpus[1]);
     WITH_LOCK (m2) {
         t2_pinned = true;
         c2.wake_all();
     }
-    t2.join();
+    t2->join();
 
 
     // Another similar test for pinning a different thread. In this
@@ -116,7 +116,7 @@ int main(int argc, char **argv)
     mutex m3;
     condvar c3;
     bool t3_pinned = false;
-    sched::thread t3([&] {
+    std::unique_ptr<sched::thread> t3(sched::thread::make([&] {
         auto now = osv::clock::uptime::now();
         while (osv::clock::uptime::now() < now + 
std::chrono::milliseconds(1000)) {
         }
@@ -126,15 +126,15 @@ int main(int argc, char **argv)
             }
         }
         expect(sched::cpu::current(), sched::cpus[1]);
-    });
-    t3.start();
+    }));
+    t3->start();
     sched::thread::sleep(std::chrono::milliseconds(1));
-    sched::thread::pin(&t3, sched::cpus[1]);
+    sched::thread::pin(&*t3, sched::cpus[1]);
     WITH_LOCK (m3) {
         t3_pinned = true;
         c3.wake_all();
     }
-    t3.join();
+    t3->join();
 
     // Test a bug we had of pinning a thread which was already on the
     // given CPU. In that case, it stays there, but needs to become
@@ -142,25 +142,25 @@ int main(int argc, char **argv)
     mutex m4;
     condvar c4;
     bool t4_pinned = false;
-    sched::thread t4([&] {
+    std::unique_ptr<sched::thread> t4(sched::thread::make([&] {
         WITH_LOCK (m4) {
             while(!t4_pinned) {
                 c4.wait(m4);
             }
         }
-    });
-    t4.start();
+    }));
+    t4->start();
     sched::thread::sleep(std::chrono::milliseconds(1));
-    expect(t4.migratable(), true);
-    auto ccpu = t4.tcpu();
-    sched::thread::pin(&t4, ccpu);
-    expect(t4.migratable(), false);
-    expect(t4.tcpu(), ccpu);
+    expect(t4->migratable(), true);
+    auto ccpu = t4->tcpu();
+    sched::thread::pin(&*t4, ccpu);
+    expect(t4->migratable(), false);
+    expect(t4->tcpu(), ccpu);
     WITH_LOCK (m4) {
         t4_pinned = true;
         c4.wake_all();
     }
-    t4.join();
+    t4->join();
 
     // Test pinning a thread several times in succession. It should work and
     // not hang (the second call shouldn't wait until the first pinning is
@@ -168,28 +168,28 @@ int main(int argc, char **argv)
     mutex m5;
     condvar c5;
     bool t5_pinned = false;
-    sched::thread t5([&] {
+    std::unique_ptr<sched::thread> t5(sched::thread::make([&] {
         WITH_LOCK (m5) {
             while(!t5_pinned) {
                 c5.wait(m5);
             }
             expect(sched::cpu::current(), sched::cpus[1]);
         }
-    });
-    t5.start();
+    }));
+    t5->start();
     sched::thread::sleep(std::chrono::milliseconds(1));
-    sched::thread::pin(&t5, sched::cpus[0]);
-    sched::thread::pin(&t5, sched::cpus[1]);
-    sched::thread::pin(&t5, sched::cpus[1]);
-    sched::thread::pin(&t5, sched::cpus[0]);
-    sched::thread::pin(&t5, sched::cpus[1]);
-    expect(t5.migratable(), false);
-    expect(t5.tcpu(), sched::cpus[1]);
+    sched::thread::pin(&*t5, sched::cpus[0]);
+    sched::thread::pin(&*t5, sched::cpus[1]);
+    sched::thread::pin(&*t5, sched::cpus[1]);
+    sched::thread::pin(&*t5, sched::cpus[0]);
+    sched::thread::pin(&*t5, sched::cpus[1]);
+    expect(t5->migratable(), false);
+    expect(t5->tcpu(), sched::cpus[1]);
     WITH_LOCK (m5) {
         t5_pinned = true;
         c5.wake_all();
     }
-    t5.join();
+    t5->join();
 
 
 
diff --git a/tests/tst-preempt.cc b/tests/tst-preempt.cc
index cdb40bf..b430a73 100644
--- a/tests/tst-preempt.cc
+++ b/tests/tst-preempt.cc
@@ -24,7 +24,7 @@ int main(int argc, char **argv)
     // section, leading to non-zero preempt_counter initialization.
     assert(sched::get_preempt_counter() == 0);
 
-    auto t1 = new sched::thread([] {
+    auto t1 = sched::thread::make([] {
             assert(sched::get_preempt_counter() == 0);
     });
     t1->start();
diff --git a/tests/tst-rcu-hashtable.cc b/tests/tst-rcu-hashtable.cc
index 89001f6..a566f37 100644
--- a/tests/tst-rcu-hashtable.cc
+++ b/tests/tst-rcu-hashtable.cc
@@ -164,7 +164,7 @@ BOOST_AUTO_TEST_CASE(test_rcu_hashtable) {
             std::vector<std::unique_ptr<sched::thread>> reader_threads;
             std::atomic<bool> running = { true };
             for (size_t i = 0; i < nr_threads; ++i) {
-                reader_threads.emplace_back(new sched::thread([&] {
+                reader_threads.emplace_back(sched::thread::make([&] {
                     do_reads(ht, range, running, status);
                 }));
             }
diff --git a/tests/tst-rcu-list.cc b/tests/tst-rcu-list.cc
index 053559d..00168bf 100644
--- a/tests/tst-rcu-list.cc
+++ b/tests/tst-rcu-list.cc
@@ -91,7 +91,7 @@ BOOST_AUTO_TEST_CASE(test_rcu_list) {
         osv::rcu_list<test_element> list;
         std::vector<std::unique_ptr<sched::thread>> readers;
         for (int i = 0; i < 20; ++i) {
-            readers.emplace_back(new sched::thread([&] { do_reads(list, 
running, sem); }));
+            readers.emplace_back(sched::thread::make([&] { do_reads(list, 
running, sem); }));
             readers.back()->start();
         }
         do_writes(list, 1000000);
diff --git a/tests/tst-threadcomplete.cc b/tests/tst-threadcomplete.cc
index 409d426..71c8af1 100644
--- a/tests/tst-threadcomplete.cc
+++ b/tests/tst-threadcomplete.cc
@@ -101,7 +101,7 @@ void do_heap_test(bool quick)
     // triggers the race.
     for (int j = 0; j < 100; ++j) {
         sched::thread *t2 = nullptr;
-        sched::thread *t1 = new sched::thread([&]{
+        sched::thread *t1 = sched::thread::make([&]{
             // wait for the t2 object to exist (not necessarily run)
             sched::thread::wait_until([&] { return t2 != nullptr; });
             if (quick) {
@@ -111,7 +111,7 @@ void do_heap_test(bool quick)
             sched::thread::sleep(10_ms);
         }, sched::thread::attr().pin(sched::cpus[0]));
 
-        t2 = new sched::thread([&]{
+        t2 = sched::thread::make([&]{
             t1->wake();
         }, sched::thread::attr().pin(sched::cpus[1]));
 
diff --git a/tests/tst-vfs.cc b/tests/tst-vfs.cc
index fa8d842..44d2c32 100644
--- a/tests/tst-vfs.cc
+++ b/tests/tst-vfs.cc
@@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE(test_concurrent_file_operations)
     std::cerr << "test1, with " << N << " threads\n";
     sched::thread *threads[N];
     for (int i = 0; i < N; i++) {
-            threads[i] = new sched::thread([] {
+            threads[i] = sched::thread::make([] {
                     struct stat buf;
                     for (int j = 0; j < 1000; j++) {
                         BOOST_REQUIRE(stat("/tests/tst-vfs.so", &buf)==0);
diff --git a/tests/tst-wait-for.cc b/tests/tst-wait-for.cc
index f64bd86..f09d943 100644
--- a/tests/tst-wait-for.cc
+++ b/tests/tst-wait-for.cc
@@ -48,15 +48,15 @@ BOOST_AUTO_TEST_CASE(test_waitqueue_1)
     mutex mtx;
     int counter = 0;
     WITH_LOCK(mtx) {
-        sched::thread waker([&] {
+        std::unique_ptr<sched::thread> waker(sched::thread::make([&] {
             WITH_LOCK(mtx) {
                 ++counter;
                 wq.wake_one(mtx);
             }
-        });
-        waker.start();
+        }));
+        waker->start();
         wq.wait(mtx);
-        waker.join();
+        waker->join();
     }
     BOOST_REQUIRE(counter == 1);
 }
@@ -69,14 +69,14 @@ BOOST_AUTO_TEST_CASE(test_waitqueue_2)
     sched::timer tmr(*sched::thread::current());
     WITH_LOCK(mtx) {
         tmr.set(500_ms);
-        sched::thread waker([&] {
+        std::unique_ptr<sched::thread> waker(sched::thread::make([&] {
             sched::thread::sleep(1_s);
             WITH_LOCK(mtx) {
                 ++counter;
                 wq.wake_one(mtx);
             }
-        });
-        waker.start();
+        }));
+        waker->start();
         // timer wait
         sched::thread::wait_for(mtx, wq, tmr);
         BOOST_REQUIRE(tmr.expired());
@@ -89,7 +89,7 @@ BOOST_AUTO_TEST_CASE(test_waitqueue_2)
         tmr.cancel();
         sched::thread::wait_for(mtx, wq, tmr);
         BOOST_REQUIRE(counter == 1);
-        waker.join();
+        waker->join();
     }
 }
 
@@ -97,19 +97,19 @@ BOOST_AUTO_TEST_CASE(test_wait_for_predicate)
 {
     std::atomic<bool> x = { false };
     auto sleeper = sched::thread::current();
-    sched::thread waker([&] {
+    std::unique_ptr<sched::thread> waker(sched::thread::make([&] {
         sched::thread::sleep(1_s);
         x.store(true);
         sleeper->wake();
-    });
-    waker.start();
+    }));
+    waker->start();
     // send some spurious wakeups for fun
-    sched::thread false_waker([&] {
+    std::unique_ptr<sched::thread> false_waker(sched::thread::make([&] {
         for (auto i = 0; i < 100; ++i) {
             sched::thread::sleep(100_ms);
             sleeper->wake();
         }
-    });
+    }));
     sched::timer tmr(*sched::thread::current());
     tmr.set(500_ms);
     sched::thread::wait_for(tmr, [&] { return x.load(); });
@@ -119,8 +119,8 @@ BOOST_AUTO_TEST_CASE(test_wait_for_predicate)
     sched::thread::wait_for(tmr, [&] { return x.load(); });
     BOOST_REQUIRE(!tmr.expired());
     BOOST_REQUIRE(x.load());
-    waker.join();
-    false_waker.join();
+    waker->join();
+    false_waker->join();
 }
 
 OSV_ELF_MLOCK_OBJECT();
diff --git a/tests/tst-yield.cc b/tests/tst-yield.cc
index ec37fd3..762493f 100644
--- a/tests/tst-yield.cc
+++ b/tests/tst-yield.cc
@@ -17,7 +17,7 @@ int main(int argc, char **argv)
     constexpr int J = 10000000;
     sched::thread *ts[N];
     for (auto &t : ts) {
-            t = new sched::thread([] {
+            t = sched::thread::make([] {
                 for (int j = 0; j < J; j++) {
                     sched::thread::yield();
                 }
-- 
2.7.4

-- 
You received this message because you are subscribed to the Google Groups "OSv 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to osv-dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to