/* -*- mode: c++; indent-tabs-mode: nil -*- */
/*
  qore_thread_intern.h

  POSIX thread library for Qore

  Qore Programming Language

  Copyright 2003 - 2013 David Nichols

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#ifndef _QORE_QORE_THREAD_INTERN_H
#define _QORE_QORE_THREAD_INTERN_H

#include <vector>
#include <set>
#include <map>

#ifndef QORE_THREAD_STACK_SIZE
#define QORE_THREAD_STACK_SIZE 1024*512
#endif

// the values here are subject to change and come from purely empirical testing
#ifndef QORE_STACK_GUARD
#ifdef CPU_X86_64
// for some reason we need 20K of stack guard on x86_64
#define QORE_STACK_GUARD (1024 * 20)
#else

#ifdef SPARC
// also we need at least 22K of stack guard on sparc for background threads for some reason
#define QORE_STACK_GUARD (1024 * 22)
#else

#if defined(HPUX) && !defined(__ia64) && !defined(__LP64__)
// need 10KB on HPUX on 32-bit pa-risc
#define QORE_STACK_GUARD (1024 * 10)
#else

// "generic" value (tested on OSX i386 and ppc and Linux i386)
#define QORE_STACK_GUARD (1024 * 8)
#endif // HPUX

#endif // SPARC
#endif // CPU_X86_64
#endif // QORE_STACK_GUARD

class Operator;
class Context;
class CVNode;
class CallNode;
class CallStack;
class LocalVarValue;
class ClosureParseEnvironment;
class ThreadSafeLocalVarRuntimeEnvironment;
struct ClosureVarValue;
class VLock;
class ConstantEntry;
class qore_ns_private;
class qore_root_ns_private;
class qore_class_private;
class AbstractQoreFunctionVariant;
class AbstractQoreZoneInfo;
class ThreadProgramData;

DLLLOCAL extern Operator* OP_BACKGROUND;

class VNode;
class AbstractQoreZoneInfo;
class ThreadData;

struct ModuleContextNamespaceCommit {
   qore_ns_private* parent;
   qore_ns_private* nns;

   DLLLOCAL ModuleContextNamespaceCommit(qore_ns_private* n_parent, qore_ns_private* n_nns) : parent(n_parent), nns(n_nns) {
   }
};

typedef std::vector<ModuleContextNamespaceCommit> mcnl_t;

class ModuleContextNamespaceList : public mcnl_t {
private:
   // not implemented
   DLLLOCAL ModuleContextNamespaceList(const ModuleContextNamespaceList&);
   
public:
   DLLLOCAL ModuleContextNamespaceList() {
   }

   DLLLOCAL ~ModuleContextNamespaceList() {
      assert(empty());
   }

   DLLLOCAL void clear();
};

struct ModuleContextFunctionCommit {
   qore_ns_private* parent;
   const char* name;
   AbstractQoreFunctionVariant* v;

   DLLLOCAL ModuleContextFunctionCommit(qore_ns_private* n_parent, const char* n_name, AbstractQoreFunctionVariant* n_v) : parent(n_parent), name(n_name), v(n_v) {
   }
};

typedef std::vector<ModuleContextFunctionCommit> mcfl_t;

class ModuleContextFunctionList : public mcfl_t {
private:
   // not implemented
   DLLLOCAL ModuleContextFunctionList(const ModuleContextFunctionList&);
   
public:
   DLLLOCAL ModuleContextFunctionList() {
   }

   DLLLOCAL ~ModuleContextFunctionList() {
      assert(empty());
   }

   DLLLOCAL void clear();
};

class QoreModuleContext {
protected:
   const char* name;
   qore_root_ns_private* rns;
   QoreStringNode* err;
   ExceptionSink& xsink;

public:
   ModuleContextNamespaceList mcnl;
   ModuleContextFunctionList mcfl;

   DLLLOCAL QoreModuleContext(const char* n, qore_root_ns_private* n_rns, ExceptionSink& xs) : name(n), rns(n_rns), err(0), xsink(xs) {
   }

   DLLLOCAL ~QoreModuleContext() {
      assert(!err);
   }

   DLLLOCAL void error(const char* fmt, ...);

   DLLLOCAL bool hasError() const {
      return xsink;
   }

   DLLLOCAL void commit();

   DLLLOCAL void rollback() {
      mcnl.clear();
      mcfl.clear();
   }

   DLLLOCAL qore_root_ns_private* getRootNS() {
      return rns;
   }
};

class QoreClosureParseNode;

class QoreModuleDefContext {
protected:
   DLLLOCAL void initClosure(AbstractQoreNode*& c, const char* n);

public:
   typedef std::set<std::string> strset_t;
   typedef std::map<std::string, std::string> strmap_t;

   AbstractQoreNode* init_c, // the initialization closure
      * del_c;               // the destructor closure
   bool name_set;

   DLLLOCAL QoreModuleDefContext() : init_c(0), del_c(0), name_set(false) {
   }

   DLLLOCAL ~QoreModuleDefContext() {
      if (init_c)
         init_c->deref(0);
      if (del_c)
         del_c->deref(0);
   }

   // set of valid tags
   static strset_t vset;

   // set of tag definitions
   strmap_t vmap;

   DLLLOCAL void set(const char* key, const AbstractQoreNode* val);

   DLLLOCAL const char* get(const char* str) const {
      strmap_t::const_iterator i = vmap.find(str);
      return i == vmap.end() || i->second.empty() ? 0 : i->second.c_str();
   }

   DLLLOCAL void parseInit();
   DLLLOCAL int init(QoreProgram& pgm, ExceptionSink& xsink);
   DLLLOCAL void checkName();

   DLLLOCAL QoreClosureParseNode* takeDel();
};

// returns 0 if the last mark has been cleared, -1 if there are more marks to check
DLLLOCAL int purge_thread_resources_to_mark(ExceptionSink* xsink);
DLLLOCAL void purge_thread_resources(ExceptionSink* xsink);
DLLLOCAL void mark_thread_resources();
DLLLOCAL void beginParsing(const char* file, void* ps = NULL, const char* src = 0, int offset = 0);
DLLLOCAL void* endParsing();
DLLLOCAL Context* get_context_stack();
DLLLOCAL void update_context_stack(Context* cstack);

DLLLOCAL QoreProgramLocation get_runtime_location();
DLLLOCAL void update_runtime_location(const QoreProgramLocation& loc);

DLLLOCAL void update_parse_line_location(int start_line, int end_line);
DLLLOCAL void set_parse_file_info(QoreProgramLocation& loc);
DLLLOCAL const char* get_parse_code();
DLLLOCAL QoreProgramLocation get_parse_location();
DLLLOCAL void update_parse_location(const QoreProgramLocation& loc);

DLLLOCAL RootQoreNamespace* getRootNS();
DLLLOCAL int64 getParseOptions();
DLLLOCAL void updateCVarStack(CVNode* ncvs);
DLLLOCAL CVNode* getCVarStack();
DLLLOCAL void updateVStack(VNode* nvs);
DLLLOCAL VNode* getVStack();
DLLLOCAL void setParseClass(QoreClass* c);
DLLLOCAL QoreClass* getParseClass();
DLLLOCAL void substituteObjectIfEqual(QoreObject* o);
DLLLOCAL QoreObject* substituteObject(QoreObject* o);
DLLLOCAL QoreException* catchSwapException(QoreException* e);
DLLLOCAL QoreException* catchGetException();
DLLLOCAL VLock *getVLock();
DLLLOCAL void end_signal_thread(ExceptionSink* xsink);
DLLLOCAL void delete_thread_local_data();
DLLLOCAL void parse_cond_push(bool mark = false);
DLLLOCAL bool parse_cond_else();
DLLLOCAL bool parse_cond_pop();
DLLLOCAL bool parse_cond_test();
DLLLOCAL void push_parse_options();
DLLLOCAL void parse_try_module_inc();
DLLLOCAL bool parse_try_module_dec();

DLLLOCAL void parse_push_name(const char* name);
DLLLOCAL std::string parse_pop_name();

DLLLOCAL qore_ns_private* parse_set_ns(qore_ns_private* ns);
DLLLOCAL qore_ns_private* parse_get_ns();
DLLLOCAL void set_module_context(QoreModuleContext* qmc);
DLLLOCAL QoreModuleContext* get_module_context();
DLLLOCAL QoreModuleDefContext* set_module_def_context(QoreModuleDefContext* qmd);
DLLLOCAL QoreModuleDefContext* get_module_def_context();

DLLLOCAL void set_thread_tz(const AbstractQoreZoneInfo* tz);
DLLLOCAL const AbstractQoreZoneInfo* get_thread_tz(bool& set);
DLLLOCAL void clear_thread_tz();

DLLLOCAL ThreadProgramData* get_thread_program_data();

DLLLOCAL int thread_ref_set(const lvalue_ref* r);
DLLLOCAL void thread_ref_remove(const lvalue_ref* r);

// pushes a new argv reference counter
DLLLOCAL void new_argv_ref();

// increments the parse argv reference counter
DLLLOCAL void inc_argv_ref();

// pushes an "ignore numeric reference" context
DLLLOCAL void push_ignore_numeric_argv_ref();

// pops an "ignore numeric reference" context
DLLLOCAL void pop_ignore_numeric_argv_ref();

// increments the parse argv reference counter for numeric references (ex: $1)
DLLLOCAL void inc_numeric_argv_ref();

// gets the parse argv reference counter and pops the context
DLLLOCAL int get_pop_argv_ref();

// clears the argv reference stack
DLLLOCAL void clear_argv_ref();

// ignore further local references to $argv
//DLLLOCAL void ignore_local_argv();

DLLLOCAL int set_constant(ConstantEntry *ce);
DLLLOCAL void remove_constant(ConstantEntry *ce);

DLLLOCAL void parseSetCodeInfo(const char* parse_code, const QoreTypeInfo* returnTypeInfo, const char*& old_code, const QoreTypeInfo*& old_returnTypeInfo);
DLLLOCAL void parseRestoreCodeInfo(const char* parse_code, const QoreTypeInfo* returnTypeInfo);
// sets the new type and returns the old
DLLLOCAL const QoreTypeInfo* saveReturnTypeInfo(const QoreTypeInfo* returnTypeInfo);
DLLLOCAL const QoreTypeInfo* getReturnTypeInfo();

#ifdef QORE_RUNTIME_THREAD_STACK_TRACE
DLLLOCAL void pushCall(CallNode* cn);
DLLLOCAL void popCall(ExceptionSink* xsink);
DLLLOCAL CallStack *getCallStack();
DLLLOCAL QoreListNode* getCallStackList();
#else
#ifdef __GNUC__
#define pushCall(args...)
#else
#define pushCall(args, ...)
#endif
#define popCall(x)
#endif

class QoreParseClassHelper {
protected:
   QoreClass* old;
   qore_ns_private* oldns;
   bool rn; // restore namespace

public:
   DLLLOCAL QoreParseClassHelper(QoreClass* cls);

   DLLLOCAL ~QoreParseClassHelper();
};

class QoreProgramLocationHelper {
protected:
   QoreProgramLocation loc;
public:
   DLLLOCAL QoreProgramLocationHelper() : loc(get_runtime_location()) {
   }

   DLLLOCAL ~QoreProgramLocationHelper() {
      update_runtime_location(loc);
   }
};

// acquires a TID and thread entry, returns -1 if not successful
DLLLOCAL int get_thread_entry();
// acquires TID 0 and sets up the signal thread entry, always returns 0
DLLLOCAL int get_signal_thread_entry();
DLLLOCAL void deregister_signal_thread();
DLLLOCAL void register_thread(int tid, pthread_t ptid, QoreProgram* pgm, bool foreign = false);
DLLLOCAL void deregister_thread(int tid);
DLLLOCAL void delete_signal_thread();

// returns 1 if data structure is already on stack, 0 if not (=OK)
DLLLOCAL int thread_push_container(const AbstractQoreNode* n);
DLLLOCAL void thread_pop_container(const AbstractQoreNode* n);

// called when a StatementBlock has "on block exit" blocks
DLLLOCAL void pushBlock(block_list_t::iterator i);
// called when a StatementBlock has "on block exit" blocks
DLLLOCAL block_list_t::iterator popBlock();
// called by each "on_block_exit" statement to activate it's code for the block exit
DLLLOCAL void advanceOnBlockExit();

DLLLOCAL LocalVarValue *thread_instantiate_lvar();
DLLLOCAL void thread_uninstantiate_lvar(ExceptionSink* xsink);

DLLLOCAL void thread_set_closure_parse_env(ClosureParseEnvironment* cenv);
DLLLOCAL ClosureParseEnvironment* thread_get_closure_parse_env();

DLLLOCAL ClosureVarValue *thread_instantiate_closure_var(const char* id, const QoreTypeInfo* typeInfo, QoreValue& nval);
DLLLOCAL void thread_uninstantiate_closure_var(ExceptionSink* xsink);
DLLLOCAL ClosureVarValue *thread_find_closure_var(const char* id);
DLLLOCAL ClosureVarValue *thread_get_runtime_closure_var(const LocalVar* id);

DLLLOCAL ThreadSafeLocalVarRuntimeEnvironment* thread_get_runtime_closure_env();
DLLLOCAL void thread_set_runtime_closure_env(ThreadSafeLocalVarRuntimeEnvironment* cenv);

DLLLOCAL int get_implicit_element();
DLLLOCAL int save_implicit_element(int n_element);

DLLLOCAL void save_global_vnode(VNode* vn);
DLLLOCAL VNode* get_global_vnode();

class QoreContainerHelper {
   const AbstractQoreNode* n;
   bool err;

public:
   DLLLOCAL QoreContainerHelper(const AbstractQoreNode* n_n) {
      // FIXME! need to have an AbstactQoreNode::isContainer() function!
      qore_type_t t = n_n ? n_n->getType() : NT_NOTHING;
      if ((t == NT_LIST || t == NT_HASH || t == NT_OBJECT || t >= QORE_NUM_TYPES)) {
	 if (!thread_push_container(n_n)) {
	    n = n_n;	    
	    err = false;
	 }
	 else {
	    n = 0;
	    err = true;
	 }
      }
      else {
	 n = 0;
	 err = false;
      }
   }
   DLLLOCAL ~QoreContainerHelper() {
      if (n)
	 thread_pop_container(n);
   }
   DLLLOCAL operator bool () const {
      return !err;
   }
};

class ThreadSafeLocalVarRuntimeEnvironmentHelper {
private:
   ThreadSafeLocalVarRuntimeEnvironment* cenv;
      
public:
   DLLLOCAL ThreadSafeLocalVarRuntimeEnvironmentHelper(ThreadSafeLocalVarRuntimeEnvironment* n_cenv) : cenv(n_cenv ? thread_get_runtime_closure_env() : 0) {
      if (n_cenv)
         thread_set_runtime_closure_env(n_cenv);
   }
   
   DLLLOCAL ~ThreadSafeLocalVarRuntimeEnvironmentHelper() {
      if (cenv)
         thread_set_runtime_closure_env(cenv);
   }
};

DLLLOCAL const QoreListNode* thread_get_implicit_args();

DLLLOCAL LocalVarValue *thread_find_lvar(const char* id);

// to get the current runtime object
DLLLOCAL QoreObject* runtime_get_stack_object();
// to get the current runtime class
DLLLOCAL const qore_class_private* runtime_get_class();
// for methods that behave differently when called within the method itself (methodGate(), memberGate(), etc)
DLLLOCAL bool runtime_in_object_method(const char* name, const QoreObject* o);

struct ClassObj {
protected:
   size_t ptr;

public:
   DLLLOCAL ClassObj() : ptr(0) {
   }

   DLLLOCAL explicit ClassObj(int p) : ptr(0) {
      assert(!p);
   }

   DLLLOCAL ClassObj(const QoreObject* o) : ptr((size_t)o) {
   }

   DLLLOCAL explicit ClassObj(const qore_class_private* qc) : ptr(qc ? (((size_t)qc) | 1) : 0) {
   }

   DLLLOCAL ClassObj(const ClassObj& old) : ptr(old.ptr) {
   }

   DLLLOCAL operator bool() const {
      return (bool)ptr;
   }

   DLLLOCAL ClassObj& operator=(const ClassObj& n) {
      ptr = n.ptr;
      return *this;
   }

   DLLLOCAL ClassObj& operator=(const QoreObject* o) {
      ptr = (size_t)o;
      return *this;
   }

   DLLLOCAL ClassObj& operator=(const qore_class_private* qc) {
      ptr = qc ? (((size_t)qc) | 1) : 0;
      return *this;
   }

   DLLLOCAL void clear() {
      ptr = 0;
   }

   /*
   DLLLOCAL bool isClass() const {
      return ptr & 1;
   }

   DLLLOCAL bool isObject() const {
      return !(ptr & 1);
   }
   */

   DLLLOCAL QoreObject* getObj() const {
      return (!(ptr & 1)) ? (QoreObject*)ptr : 0;
   }

   DLLLOCAL const qore_class_private* getClass() const;
};

struct ClosureVarValue;

class lvalue_ref {
public:
   AbstractQoreNode* vexp;
   QoreObject* self;
   QoreProgram* pgm;

   DLLLOCAL lvalue_ref(AbstractQoreNode* n_lvexp, QoreObject* n_self) : vexp(n_lvexp), self(n_self), pgm(getProgram()) {
      if (self)
         self->tRef();
   }

   DLLLOCAL lvalue_ref(const lvalue_ref& old) : vexp(old.vexp->refSelf()), self(old.self), pgm(old.pgm) {
      if (self)
         self->tRef();
   }

   DLLLOCAL ~lvalue_ref() {
      if (self)
         self->tDeref();
      if (vexp)
         vexp->deref(0);
   }

   DLLLOCAL void del(ExceptionSink* xsink) {
      if (vexp) {
         vexp->deref(xsink);
         vexp = 0;
      }
   }

   static lvalue_ref* get(const ReferenceNode* r) {
      return r->priv;
   }
};

class CodeContextHelper {
private:
   const char* old_code;
   ClassObj old;
   ExceptionSink* xsink;

public:
   DLLLOCAL CodeContextHelper(const char* code, ClassObj obj, ExceptionSink* xs);
   DLLLOCAL ~CodeContextHelper();
};

class ObjectSubstitutionHelper {
private:
   ClassObj old;

public:
   DLLLOCAL ObjectSubstitutionHelper(QoreObject* obj);
   DLLLOCAL ~ObjectSubstitutionHelper();
};

typedef std::map<const LocalVar*, ClosureVarValue*> cvar_map_t;

class ThreadSafeLocalVarRuntimeEnvironment {
private:
   cvar_map_t cmap;

public:
   DLLLOCAL ThreadSafeLocalVarRuntimeEnvironment(const lvar_set_t* vlist);
   DLLLOCAL ~ThreadSafeLocalVarRuntimeEnvironment();
   DLLLOCAL ClosureVarValue* find(const LocalVar* id);
   DLLLOCAL void del(ExceptionSink* xsink);
};

struct ThreadLocalProgramData;

class ProgramThreadCountContextHelper {
protected:
   QoreProgram* old_pgm;
   ThreadLocalProgramData* old_tlpd;
   bool restore;

public:
   DLLLOCAL ProgramThreadCountContextHelper(ExceptionSink* xsink, QoreProgram* pgm, bool runtime);
   DLLLOCAL ~ProgramThreadCountContextHelper();
};

//int thread_ref_set(const lvalue_ref* r);
//void thread_ref_remove(const lvalue_ref* r);

class RuntimeReferenceHelperBase {
protected:
   const lvalue_ref* ref;
   ProgramThreadCountContextHelper pch;
   ObjectSubstitutionHelper osh;
   ExceptionSink* xsink;

public:
   DLLLOCAL RuntimeReferenceHelperBase(const lvalue_ref& r, ExceptionSink* n_xsink)
      : ref(&r), pch(n_xsink, r.pgm, true), osh(r.self), xsink(n_xsink) {
      //printd(5, "RuntimeReferenceHelperBase::RuntimeReferenceHelperBase() this: %p vexp: %p %s %d\n", this, r.vexp, get_type_name(r.vexp), get_node_type(r.vexp));
      if (thread_ref_set(&r)) {
         ref = 0;
         xsink->raiseException("CIRCULAR-REFERENCE-ERROR", "a circular lvalue reference was detected");
      }
   }

   DLLLOCAL ~RuntimeReferenceHelperBase() {
      if (ref)
         thread_ref_remove(ref);
   }

   DLLLOCAL operator bool() const {
      return !(*xsink);
   }
};

class RuntimeReferenceHelper : public RuntimeReferenceHelperBase {
public:
   DLLLOCAL RuntimeReferenceHelper(const ReferenceNode& r, ExceptionSink* n_xsink) : RuntimeReferenceHelperBase(*lvalue_ref::get(&r), n_xsink) {
   }

   DLLLOCAL RuntimeReferenceHelper(const lvalue_ref& r, ExceptionSink* n_xsink) : RuntimeReferenceHelperBase(r, n_xsink) {
   }
};

class ArgvContextHelper {
private:
   QoreListNode* old_argv;
   ExceptionSink* xsink;
   
public:
   DLLLOCAL ArgvContextHelper(QoreListNode* argv, ExceptionSink* n_xsink);
   // calls deref(xsink) on list in destructor
   DLLLOCAL ~ArgvContextHelper();
};

class SingleArgvContextHelper {
private:
   QoreListNode* old_argv;
   ExceptionSink* xsink;
   
public:
   DLLLOCAL SingleArgvContextHelper(const AbstractQoreNode* val, ExceptionSink* n_xsink);
   // calls deref(xsink) on list in destructor
   DLLLOCAL ~SingleArgvContextHelper();
};

class ImplicitElementHelper {
private:
   int element;

public:
   DLLLOCAL ImplicitElementHelper(int n_element) : element(save_implicit_element(n_element)) {
   }
   DLLLOCAL ~ImplicitElementHelper() {
      save_implicit_element(element);
   }
};

#ifdef QORE_RUNTIME_THREAD_STACK_TRACE
class CallNode {
public:
   const char* func;
   QoreProgramLocation loc;
   int type;

   ClassObj obj;
   CallNode* next, *prev;

   DLLLOCAL CallNode(const char* f, int t, ClassObj o);
   DLLLOCAL void objectDeref(ExceptionSink* xsink);
   DLLLOCAL QoreHashNode* getInfo() const;
};

class CallStack {
private:
   CallNode* tail;

public:
   DLLLOCAL CallStack();
   DLLLOCAL ~CallStack();
   DLLLOCAL QoreListNode* getCallStack() const;
   DLLLOCAL void push(CallNode* cn);
   DLLLOCAL void pop(ExceptionSink* xsink);
   DLLLOCAL void substituteObjectIfEqual(QoreObject* o);
   DLLLOCAL QoreObject* getStackObject() const;
   DLLLOCAL const qore_class_private* getStackClass() const;
   DLLLOCAL QoreObject* substituteObject(QoreObject* o);
   //DLLLOCAL bool inMethod(const char* name, QoreObject* o) const;
};

class CallStackHelper : public CallNode {
   ExceptionSink* xsink;
   
   // not implemented
   DLLLOCAL CallStackHelper(const CallStackHelper&);
   DLLLOCAL CallStackHelper& operator=(const CallStackHelper&);
   DLLLOCAL void* operator new(size_t);
   
public:
   DLLLOCAL CallStackHelper(const char* f, int t, ClassObj o, ExceptionSink* n_xsink) : CallNode(f, t, o), xsink(n_xsink) {
      pushCall(this);
   }
   DLLLOCAL ~CallStackHelper() {
      popCall(xsink);
   }
};
#define CODE_CONTEXT_HELPER(type, name, self, xsink) CodeContextHelper cch_auto(name, self, xsink); CallStackHelper csh_auto(name, type, self, xsink)
#else
#define CODE_CONTEXT_HELPER(type, name, self, xsink) CodeContextHelper cch_auto(name, self, xsink)
#endif

DLLLOCAL void init_qore_threads();
DLLLOCAL QoreNamespace* get_thread_ns(QoreNamespace& qorens);
DLLLOCAL void delete_qore_threads();
DLLLOCAL QoreListNode* get_thread_list();
DLLLOCAL QoreHashNode* getAllCallStacks();

class QorePThreadAttr {
private:
   pthread_attr_t attr;

public:
   DLLLOCAL QorePThreadAttr() {
      pthread_attr_init(&attr);
      pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
   }

   DLLLOCAL ~QorePThreadAttr() {
      //printd(2, "calling pthread_attr_destroy(%08p)\n", &attr);
      pthread_attr_destroy(&attr);
      //printd(2, "returned from pthread_attr_destroy(%08p)\n", &attr);
   }

#ifdef HAVE_PTHREAD_ATTR_GETSTACK
   DLLLOCAL void getstack(void*& ptr, size_t &ssize) {
      pthread_attr_getstack(&attr, &ptr, &ssize);
   }
#endif
      
   DLLLOCAL size_t getstacksize() const {
      size_t ssize;
      pthread_attr_getstacksize(&attr, &ssize);
      return ssize;
   }

   DLLLOCAL int setstacksize(size_t ssize) {
      return pthread_attr_setstacksize(&attr, ssize);
   }

   DLLLOCAL pthread_attr_t* get_ptr() {
      return &attr;
   }
};

DLLLOCAL extern QorePThreadAttr ta_default;

#ifdef QORE_MANAGE_STACK
DLLLOCAL int check_stack(ExceptionSink* xsink);
#endif

class ParseCodeInfoHelper {
private:
   const char* parse_code;
   const QoreTypeInfo* returnTypeInfo;

public:
   DLLLOCAL ParseCodeInfoHelper(const char* n_parse_code, const QoreTypeInfo* n_returnTypeInfo) {
      parseSetCodeInfo(n_parse_code, n_returnTypeInfo, parse_code, returnTypeInfo);
   }
   DLLLOCAL ~ParseCodeInfoHelper() {
      parseRestoreCodeInfo(parse_code, returnTypeInfo);
   }
};

class NamespaceParseContextHelper {
private:
   qore_ns_private* ns;
   bool restore;

public:
   DLLLOCAL NamespaceParseContextHelper(qore_ns_private* n_ns) : ns(parse_set_ns(n_ns)), restore(ns != n_ns) {
   }
   DLLLOCAL ~NamespaceParseContextHelper() {
      if (restore)
         parse_set_ns(ns);
   }
};

class OptionalNamespaceParseContextHelper {
private:
   qore_ns_private* ns;
   bool restore;

public:
   DLLLOCAL OptionalNamespaceParseContextHelper(qore_ns_private* n_ns) {
      if (n_ns) {
         ns = parse_set_ns(n_ns);
         restore = (ns != n_ns);
      }
      else
         restore = false;
   }
   DLLLOCAL ~OptionalNamespaceParseContextHelper() {
      if (restore)
         parse_set_ns(ns);
   }
};

class ThreadData;

class ThreadProgramData : public QoreReferenceCounter {
private:
   // for the set of QoreProgram objects we have local variables in
   typedef std::set<QoreProgram*> pgm_set_t;
   pgm_set_t pgm_set;

   // lock for pgm_set data structure (which is accessed from multiple threads when QorePrograms deregister themselves)
   QoreThreadLock pslock;

   ThreadData* td;

   DLLLOCAL void ref() {
      ROreference();
   }

   DLLLOCAL ~ThreadProgramData() {
      assert(pgm_set.empty());
   }

public:
   DLLLOCAL ThreadProgramData(ThreadData* n_td) : td(n_td) {
   }

   DLLLOCAL void delProgram(QoreProgram* pgm);
   DLLLOCAL void saveProgram(bool runtime);
   DLLLOCAL void del(ExceptionSink* xsink);

   DLLLOCAL void deref() {
      if (ROdereference())
         delete this;
   }
};

DLLLOCAL extern pthread_mutexattr_t ma_recursive;

#endif
