Package: src/packages/spinlock.fdoc

Spinlocks

key file
flx_spinlock.hpp share/lib/rtl/flx_spinlock.hpp
flx_spinlock.cpp share/src/rtl/flx_spinlock.cpp
pthread_fast_lock.hpp share/lib/rtl/pthread_fast_lock.hpp
pthread_fast_lock.cpp $PWD/CRAP/src/pthread/pthread_fast_lock.cpp
pthread_fast_lock.flx $PWD/CRAP/lib/std/pthread/pthread_fast_lock.flx

Spinlock

//[flx_spinlock.hpp]
#ifndef _FLX_SPINLOCK_HPP
#define _FLX_SPINLOCK_HPP
#include "flx_rtl_config.hpp"

#include <atomic>

namespace flx { namespace rtl {

// C++ compliant Lockable
struct RTL_EXTERN flx_spinlock {
private:
  flx_spinlock(flx_spinlock const&)=delete; // no copying
  flx_spinlock(flx_spinlock &&)=delete; // no moving
  flx_spinlock &operator=(flx_spinlock const &)=delete; // no assignment

  ::std::atomic_flag volatile flag;
public:
  flx_spinlock() noexcept; // init to clear
  void lock() volatile;
  void unlock() volatile;
};

struct RTL_EXTERN spinguard {
private:
  spinguard() = delete;
  spinguard(spinguard const&) = delete;
  spinguard *operator=(spinguard const&)=delete;
  bool cond;
  ::std::atomic_flag *plock;
public:
  spinguard (bool,::std::atomic_flag *p);
  ~spinguard ();
};
}}

#endif
//[flx_spinlock.cpp]
#include "flx_spinlock.hpp"

using namespace std;
using namespace flx;
using namespace rtl;

flx_spinlock::flx_spinlock() noexcept { flag.clear(memory_order_release); }
void flx_spinlock::lock() volatile { while(flag.test_and_set(memory_order_acquire)); }
void flx_spinlock::unlock() volatile { flag.clear(memory_order_release); }


spinguard::spinguard (bool cond_, ::std::atomic_flag *p): cond(cond_), plock(p) {
  if (cond) while (plock->test_and_set(std::memory_order_acquire));
}
spinguard::~spinguard () { if (cond)plock->clear(std::memory_order_release); }

Fast Resource Lock

This is a fast application level lock to be used for serialisation of transient accessed to data structures. It is a mutex, however unlike system mutex, it is safe to use with the Felix GC.

System mutex are NOT GC safe because in Felix every allocation may potentially trigger a garbage collection which requires a world stop. Since world stops are cooperative, the collector must wait until all threads have voluntarily yielded, usually by themselves performing an allocation or an explicit call to perform a collection, but suicide should work too.

However if a thread blocks trying to lock a mutex held by another thread which is now stopped for the GC, we have a deadlock. So a user level lock must have a timeout and a spin loop which includes regular checking for a GC world stop request.

It would be acceptable if the check were done atomically with blocking on a lock request followed by another check, because locking itself does not change reachability state. With those semantics, it’s fine for the thread to block, provided the GC counts it as having yielded, and it cannot unblock during the GC. That basically means unlocking must also do the check, to ensure blocked threads stay blocked.

//[pthread_fast_lock.hpp]
/*
#ifndef __pthread_fast_lock__
#define __pthread_fast_lock__
#include "flx_pthread_config.hpp"
#include "pthread_thread_control_base.hpp"
#include <atomic>

namespace flx { namespace rtl {

class PTHREAD_EXTERN fast_lock
{
  ::std::atomic_flag flag;
  ::flx::pthread::thread_control_base_t *tc;
public:
  fast_lock(::flx::pthread::thread_control_base_t *);
  fast_lock() = delete;
  fast_lock(fast_lock const&)  = delete;
  void operator = (fast_lock const&) = delete;
  void lock();
  void unlock();
};
}}
#endif
*/
//[pthread_fast_lock.cpp]
/*
#include "pthread_fast_lock.hpp"
#include <chrono>
#include <thread>
#include <mutex>

namespace flx { namespace rtl {
fast_lock::fast_lock(::flx::pthread::thread_control_base_t *tc_) : tc(tc_) { flag.clear(); }
void fast_lock::unlock() { flag.clear(); }
void fast_lock::lock() {
  while (!flag.test_and_set())
  {
    tc->yield();
fprintf(stderr, "thread_fast_lock: thread %p calling std::this_thread::yield()",::flx::pthread::mythrid());
    ::std::this_thread::sleep_for(::std::chrono::nanoseconds (200));
  }
}

}}
*/
//[pthread_fast_lock.flx]
/*
class FastLock
{
   type fast_lock = "::flx::rtl::fast_lock*"
     requires header '#include "pthread_fast_lock.hpp"';
   ctor fast_lock : unit = "new ::flx::rtl::fast_lock(PTF gcp->collector->get_thread_control())";
   proc delete : fast_lock = "delete $1;";
   proc lock : fast_lock = "$1->lock();";
   proc unlock : fast_lock = "$1->unlock();";

}
*/