// #thread_primitives
#if OS_WINDOWS
  struct Condition_Variable {
    CONDITION_VARIABLE condition_variable;
  };
  struct Semaphore {
    HANDLE event;
  };
  struct Mutex {
    CRITICAL_SECTION csection;
  };
  struct OS_Thread {
    HANDLE windows_thread;
    s32 windows_thread_id;
  };
#endif
#define POSIX_THREADS OS_LINUX || OS_MACOS || OS_IOS || OS_ANDROID
#if OS_MACOS
  struct Semaphore {
    task_t owner;
    semaphore_t event = 0;
  };
#endif
#if OS_LINUX || OS_ANDROID
  struct Semaphore {
    sem_t semaphore;
  };
#endif
#if OS_IS_UNIX // #posix threads
  struct OS_Thread {
    pthread_t thread_handle;
    Semaphore is_alive;
    Semaphore suspended;
    b32 is_done;
  };
#endif

// really hacky forward declares.
struct Work_Entry;
struct Worker_Info;
struct Work_List;
struct Thread_Group; 
void init(Work_List* list);
void destroy(Work_List* list);

typedef s64 (*Thread_Proc)(Thread* thread); 
s64 thread_group_run (Thread* thread);
struct Thread {
  Thread_Context* context;
  Thread_Proc proc;
  void* data;
  
  s64 index;
  
  OS_Thread os_thread;
  
  // Used by Thread_Group
  Worker_Info* worker_info;
};

global u32 next_thread_index = 1;


// Thread Group API (Copied from Jonathan Blow's implementation - I did not come up with this.)

struct Work_Entry {
  Work_Entry* next;
  void* work;
  s64 thread_index; // Thread.index for the thread that handled this work
  // string logging_name;
  f64 issue_time;
  s32 work_list_index;
};
struct Work_List {
  Semaphore semaphore;
  Mutex mutex;
  Work_Entry* first;
  Work_Entry* last;
  s32 count;
};
struct Worker_Info {
  Thread thread;
  Work_List available;
  Work_List completed;
  Thread_Group* group;
  s32 worker_index;
  u8 padding0[44];
  // Work steal indices should be on another cache line:
  ArrayView<s32> work_steal_indices;
  u8 padding1[48];
};
static_assert(sizeof(Worker_Info) % 64 == 0); // This MUST be padded to cache line!
enum class Thread_Continue_Status : s32 {
  THREAD_STOP = 0,
  THREAD_CONTINUE = 1 
};
typedef Thread_Continue_Status (*Thread_Group_Proc)(Thread_Group* group, Thread* thread, void* work);
struct Thread_Group {
  void* data;
  Thread_Group_Proc proc;
  string name;
  
  Allocator allocator; // for allocating work indices
  ArrayView<Worker_Info> worker_info; // only alloc'd once with allocator??
  s32 next_worker_index;
  
  bool initialized = false;
  bool started     = false;
  bool should_exit = false;
};
