Skip to content
John Skaller edited this page Nov 28, 2018 · 1 revision

Felix has the following data types to manage threads:

namespace flx { namespace pthread {

thread_data_t

First, the thread_data_t is used to hold the thread specific data required by the garbage collector to perform a conservative scan of the stack.

struct thread_data_t {
  thread_data_t(void *b) : stack_base(b), stack_top(0), active(true) {}
  void *stack_base;
  void *stack_top;
  bool active;
};

The stack_base is set by the thread when it is created. The stack_top must be a pointer to a lower address, and is set by the thread when it yields the garbage collector due to a world_stop request. The active flag records whether the thread is running, and so the stack_top is invalid, or the thread has yielded, in which case the stack_top will be valid.

memory_range_t

This type just records a memory range from b to e. The b pointer must be lower than the e pointer.

struct memory_range_t {
  memory_range_t(void *b_, void *e_) : b(b_), e(e_) {}
  void *b;
  void *e;
};

memory_ranges_t

Finally:

typedef ::std::vector<memory_range_t> memory_ranges_t;

is a set of memory ranges, used by the GC to perform a conservative scan, each component range is the extent of the stack of a suspended Felix pthread.

world_stop_notifier

This class is used as a base for objects containing locks and condition variables. The thread control machinery keeps track of objects that must be notified in case the garbage collector requires a world_stop. It calls the notify_world_stop() method for each tracked object, to allow the object to release any locks and yield to the collector.

The derived class is required to appropriately define the notifier method as well as a destructor. The base destructor does nothing.

class PTHREAD_EXTERN world_stop_notifier_t 
{
public:
  virtual void notify_world_stop()=0;
  virtual ~world_stop_notifier_t();
};

thread_control_base_t

This is the interface of the primary class for thread management required by the garbage collector.

class PTHREAD_EXTERN thread_control_base_t
{
public:
  virtual bool get_debug() const =0;
  virtual bool world_stop() = 0;
  virtual void world_start() = 0;
  virtual void resume() = 0;
  virtual void suspend() = 0;
  virtual void yield() = 0;
  virtual void join_all() = 0;
  virtual void add_thread(void*)=0;
  virtual void remove_thread()=0;
  virtual size_t thread_count()=0;
  virtual void register_world_stop_notifier(world_stop_notifier_t *)=0;
  virtual void unregister_world_stop_notifier(world_stop_notifier_t *)=0;

  virtual ~thread_control_base_t()=0;
  virtual  memory_ranges_t *get_block_list() = 0; // caller owns result and should delete it
};

The add_thread() method is called by a newly created thread, and passed the its stack base address. It registers the thread by its native id in the thread tracking data structure.

The remove_thread() method is called by the thread just before it suicides, and unregisters the thread.

The thread_count() method reports how many threads are registered, it can be called by any client. Be aware of possible race conditions.

The world_stop() method is called by the garbage collector when it needs to do a collection. The calling thread will have invoked the collector explicitly or triggered it by trying to allocate past a dynamically determined threshhold.

The world_stop() method does not return until all other threads than its caller has yielded.

The world_start() method is called by the collector when the collection is complete. It releases threads from their suspended state and allows them to continue operating.

The suspend() method is called by a thread in response to a world stop request to notify the requestor it has suspended. It transfers the threads current stack pointer to the registry, in preparation for the collector scanning its stack.

The resume() method is called by the thread when it it is released from its suspended state to notify the collector it is running. It is used to ensure all threads are running before another world_stop can be ordered.

The join_all() method is used to terminate Felix by waiting until all threads, except the caller, have finished and exited.

The register_world_stop_notifier() method is called by any object derived from world_stop_notifier_t in its constructor, and causes the object address to be register with the world stop notification service. When a world stop is required, all objects on the registry have their notify_world_stop() method called. Typically this method will be defined to issue a signal which bumps a condition variable or lock out of its wait state so that the thread holding it can yield to the collector.

The unregister_world_stop_notifier() method should be called by a registered objects destructor to remove its registration.