From: Nadav Har'El <n...@scylladb.com>
Committer: Nadav Har'El <n...@scylladb.com>
Branch: master

sched::thread: add unpin() method

This patch adds an unpin() method to undo a pin() operation on a thread
(operating on either the current thread or on a different thread is
supported). After an unpin() operation, the thread may be migrated
to any CPU (unless, of course, a temporary migrate_disable() is in force).

This patch does not yet change sched_setaffinity() and friends to call
the new unpin() method. This will come in the next patch.

Refs #778.

Signed-off-by: Nadav Har'El <n...@scylladb.com>
Message-Id: <20170117135735.19891-1-...@scylladb.com>

---
diff --git a/core/sched.cc b/core/sched.cc
--- a/core/sched.cc
+++ b/core/sched.cc
@@ -632,6 +632,42 @@ void thread::pin(thread *t, cpu *target_cpu)
     helper->join();
 }

+void thread::unpin()
+{
+    // Unpinning the current thread is straightforward. But to work on a
+ // different thread safely, without risking races with concurrent attempts
+    // to pin, unpin, or migrate the same thread, we need to run the actual
+    // unpinning code on the same CPU as the target thread.
+    if (this == current()) {
+        WITH_LOCK(preempt_lock) {
+            if (_pinned) {
+                _pinned = false;
+                 std::atomic_signal_fence(std::memory_order_release);
+                _migration_lock_counter--;
+            }
+        }
+        return;
+    }
+    std::unique_ptr<thread> helper(thread::make([this] {
+        WITH_LOCK(preempt_lock) {
+            // helper thread started on the same CPU as "this", but by now
+ // "this" might migrated. If that happened helper need to migrate.
+            while (sched::cpu::current() != this->tcpu()) {
+                DROP_LOCK(preempt_lock) {
+                    thread::pin(this->tcpu());
+                }
+            }
+            if (_pinned) {
+                _pinned = false;
+                 std::atomic_signal_fence(std::memory_order_release);
+                _migration_lock_counter--;
+            }
+        }
+    }, sched::thread::attr().pin(tcpu())));
+    helper->start();
+    helper->join();
+}
+
 void cpu::load_balance()
 {
     notifier::fire();
diff --git a/include/osv/sched.hh b/include/osv/sched.hh
--- a/include/osv/sched.hh
+++ b/include/osv/sched.hh
@@ -495,6 +495,13 @@ public:
      * method), this is a static function taking the thread as a parameter.
      */
     static void pin(thread *t, cpu *target_cpu);
+    /**
+     * Unpin the given thread.
+     *
+ * This undoes the operation of a previous pin() operation (if any), and
+     * allows the thread to be later migrated to any CPU.
+     */
+    void unpin();

 #ifdef __OSV_CORE__
     static inline thread* current() { return s_current; };
diff --git a/tests/tst-pin.cc b/tests/tst-pin.cc
--- a/tests/tst-pin.cc
+++ b/tests/tst-pin.cc
@@ -51,7 +51,10 @@ int main(int argc, char **argv)
     expect(sched::cpu::current(), sched::cpus[1]);
     sched::thread::pin(sched::cpus[0]);
     expect(sched::cpu::current(), sched::cpus[0]);
-
+    // check that we can unpin this thread
+    expect(sched::thread::current()->pinned(), true);
+    sched::thread::current()->unpin();
+    expect(sched::thread::current()->pinned(), false);

     // Check that we can pin a different thread.
     // In this test the thread will most likely be sleeping.
@@ -185,6 +188,10 @@ int main(int argc, char **argv)
     sched::thread::pin(&*t5, sched::cpus[1]);
     expect(t5->migratable(), false);
     expect(t5->tcpu(), sched::cpus[1]);
+    // also test unpin (only need to unpin once)
+    expect(t5->pinned(), true);
+    t5->unpin();
+    expect(t5->pinned(), false);
     WITH_LOCK (m5) {
         t5_pinned = true;
         c5.wake_all();

--
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