1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-08 08:47:17 +01:00
SqMod/module/Core/Routine.hpp

679 lines
22 KiB
C++
Raw Normal View History

#pragma once
2016-02-21 08:25:46 +01:00
// ------------------------------------------------------------------------------------------------
#include "Core/Utility.hpp"
2016-02-21 08:25:46 +01:00
// ------------------------------------------------------------------------------------------------
namespace SqMod {
/* ------------------------------------------------------------------------------------------------
* Execute callbacks after specific intervals of time.
*/
class Routine
{
public:
/* --------------------------------------------------------------------------------------------
* Simplify future changes to a single point of change.
*/
typedef int64_t Time;
typedef SQInteger Interval;
typedef uint32_t Iterator;
2016-11-17 22:10:31 +01:00
typedef LightObj Argument;
2016-11-17 22:10:31 +01:00
private:
2016-02-21 08:25:46 +01:00
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* Structure that represents an active routine and keeps track of the routine information.
2016-02-21 08:25:46 +01:00
*/
2016-11-17 22:10:31 +01:00
struct Instance
{
// ----------------------------------------------------------------------------------------
LightObj mEnv; // A reference to the managed environment object.
LightObj mFunc; // A reference to the managed function object.
LightObj mInst; // Reference to the routine associated with this instance.
LightObj mData; // A reference to the arbitrary data associated with this instance.
String mTag; // An arbitrary string which represents the tag.
Iterator mIterations; // Number of iterations before self destruct.
Interval mInterval; // Interval between routine invocations.
bool mSuspended; // Whether this instance is allowed to receive calls.
bool mQuiet; // Whether this instance is allowed to handle errors.
bool mEndure; // Whether this instance is allowed to terminate itself on errors.
bool mExecuting; // Whether this instance is currently being executed.
uint8_t mArgc; // The number of arguments that the routine must forward.
2016-11-17 22:10:31 +01:00
Argument mArgv[14]; // The arguments that the routine must forward.
/* ----------------------------------------------------------------------------------------
* Default constructor.
*/
2020-03-22 05:53:04 +01:00
Instance() noexcept
2016-11-17 22:10:31 +01:00
: mEnv()
, mFunc()
, mInst()
, mData()
, mTag()
, mIterations(0)
, mInterval(0)
, mSuspended(false)
, mQuiet(GetSilenced())
, mEndure(false)
, mExecuting(false)
2016-11-17 22:10:31 +01:00
, mArgc(0)
, mArgv()
{
/* ... */
}
2016-11-17 22:10:31 +01:00
/* ----------------------------------------------------------------------------------------
* Copy constructor. (disabled)
*/
Instance(const Instance & o) = delete;
2016-11-17 22:10:31 +01:00
/* ----------------------------------------------------------------------------------------
* Move constructor. (disabled)
*/
Instance(Instance && o) = delete;
2016-11-17 22:10:31 +01:00
/* ----------------------------------------------------------------------------------------
* Destructor.
*/
~Instance()
{
Terminate();
}
2016-02-21 08:25:46 +01:00
2016-11-17 22:10:31 +01:00
/* ----------------------------------------------------------------------------------------
* Copy assignment operator. (disabled)
*/
Instance & operator = (const Instance & o) = delete;
2016-02-21 08:25:46 +01:00
2016-11-17 22:10:31 +01:00
/* ----------------------------------------------------------------------------------------
* Move assignment operator. (disabled)
*/
Instance & operator = (Instance && o) = delete;
2016-11-17 22:10:31 +01:00
/* ----------------------------------------------------------------------------------------
* Initializes the routine parameters. (assumes previous values are already released)
*/
void Init(HSQOBJECT & env, HSQOBJECT & func, HSQOBJECT & inst, Interval intrv, Iterator itr)
{
// Initialize the callback objects
mEnv = LightObj{env};
mFunc = LightObj{func};
2016-11-17 22:10:31 +01:00
// Associate with the routine instance
mInst = LightObj{inst};
2016-11-17 22:10:31 +01:00
// Initialize the routine options
mIterations = itr;
mInterval = intrv;
// This can't be true now
mExecuting = false;
2016-11-17 22:10:31 +01:00
}
2016-11-17 22:10:31 +01:00
/* ----------------------------------------------------------------------------------------
* Release managed script resources.
*/
void Release()
{
mEnv.Release();
mFunc.Release();
mInst.Release();
mData.Release();
mIterations = 0;
mInterval = 0;
mTag.clear();
}
2016-11-17 22:10:31 +01:00
/* ----------------------------------------------------------------------------------------
* Execute the managed routine.
*/
Interval Execute()
{
// Is this even a valid routine?
2016-11-17 22:10:31 +01:00
if (mInst.IsNull())
{
return 0; // Dunno how we got here but it ends now
}
// Are we allowed to forward calls?
else if (!mSuspended)
{
// Grab the virtual machine once
HSQUIRRELVM vm = SqVM();
2016-11-17 22:10:31 +01:00
// Push the function on the stack
sq_pushobject(vm, mFunc);
// Push the environment on the stack
if (!mEnv.IsNull())
{
sq_pushobject(vm, mEnv); // Push object
}
else
{
sq_pushobject(vm, mInst); // Push self
}
2016-11-17 22:10:31 +01:00
// Push function parameters, if any
for (uint32_t n = 0; n < mArgc; ++n)
2016-11-17 22:10:31 +01:00
{
sq_pushobject(vm, mArgv[n].mObj);
}
// This routine is currently executing
mExecuting = true;
2016-11-17 22:10:31 +01:00
// Make the function call and store the result
2020-03-22 05:53:04 +01:00
const SQRESULT res = sq_call(vm, mArgc + 1, static_cast< SQBool >(false), static_cast< SQBool >(!mQuiet));
// This routine has finished executing
mExecuting = false;
// Pop the callback object from the stack
sq_pop(vm, 1);
2016-11-17 22:10:31 +01:00
// Validate the result
if (SQ_FAILED(res))
{
// Should we endure the errors?
if (!mEndure)
{
Terminate(); // Destroy our self on error
}
2016-11-17 22:10:31 +01:00
}
}
// Decrease the number of iterations if necessary
if (mIterations && (--mIterations) == 0)
2016-11-17 22:10:31 +01:00
{
Terminate(); // This routine reached the end of it's life
}
// Return the current interval
return mInterval;
}
2016-11-17 22:10:31 +01:00
/* ----------------------------------------------------------------------------------------
* Clear the arguments.
*/
void Clear()
{
// Now release the arguments
for (auto & a : mArgv)
{
a.Release();
}
// Reset the counter
mArgc = 0;
}
2016-11-17 22:10:31 +01:00
/* ----------------------------------------------------------------------------------------
* Terminate the routine.
*/
void Terminate()
{
2016-11-17 22:10:31 +01:00
Release();
Clear();
}
2016-11-17 22:10:31 +01:00
};
private:
2016-02-21 08:25:46 +01:00
2016-11-17 22:10:31 +01:00
// --------------------------------------------------------------------------------------------
static Time s_Last; // Last time point.
static Time s_Prev; // Previous time point.
static Interval s_Intervals[SQMOD_MAX_ROUTINES]; // List of intervals to be processed.
static Instance s_Instances[SQMOD_MAX_ROUTINES]; // List of routines to be executed.
static bool s_Silenced; // Error reporting independent from global setting.
2016-02-21 08:25:46 +01:00
private:
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* The index of the slot in the pool of active routines.
*/
uint32_t m_Slot;
2016-11-17 22:10:31 +01:00
protected:
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* Default constructor.
*/
2016-11-17 22:10:31 +01:00
Routine()
: m_Slot(SQMOD_MAX_ROUTINES)
{
/* ... */
}
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* Default constructor.
*/
explicit Routine(uint32_t slot)
2016-11-17 22:10:31 +01:00
: m_Slot(slot)
{
/* ... */
}
2020-03-22 05:53:04 +01:00
/* --------------------------------------------------------------------------------------------
* Find an unoccupied routine slot.
*/
static SQInteger FindUnused()
{
for (const auto & r : s_Instances)
{
// Either not used or not currently being executing
if (r.mInst.IsNull() && !(r.mExecuting))
2020-03-22 05:53:04 +01:00
{
return (&r - s_Instances); // Return the index of this element
}
}
// No available slot
return -1;
}
public:
/* --------------------------------------------------------------------------------------------
* Default constructor.
*/
~Routine()
{
if (m_Slot < SQMOD_MAX_ROUTINES)
{
Terminate();
}
}
/* --------------------------------------------------------------------------------------------
* Copy constructor. (disabled)
*/
Routine(const Routine & o) = delete;
/* --------------------------------------------------------------------------------------------
* Move constructor. (disabled)
*/
Routine(Routine && o) = delete;
/* --------------------------------------------------------------------------------------------
* Copy assignment operator. (disabled)
*/
Routine & operator = (const Routine & o) = delete;
/* --------------------------------------------------------------------------------------------
* Move assignment operator. (disabled)
*/
Routine & operator = (Routine && o) = delete;
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* Retrieve the number of used routine slots.
*/
2016-11-17 22:10:31 +01:00
static SQInteger GetUsed()
{
2016-11-17 22:10:31 +01:00
SQInteger n = 0;
// Iterate routine list
for (const auto & r : s_Instances)
{
2016-11-17 22:10:31 +01:00
if (!r.mInst.IsNull())
{
++n;
}
}
2016-11-17 22:10:31 +01:00
// Return the final count
return n;
}
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* Retrieve the number of used routine slots.
*/
static const LightObj & FindByTag(StackStrF & tag)
{
// Is the specified tag valid?
2016-11-17 22:10:31 +01:00
if (!tag.mPtr)
{
STHROWF("Invalid routine tag");
}
2016-11-17 22:10:31 +01:00
// Iterate routine list
for (const auto & r : s_Instances)
{
2020-03-22 05:53:04 +01:00
if (!r.mInst.IsNull() && r.mTag == tag.mPtr)
2016-11-17 22:10:31 +01:00
{
return r.mInst; // Return this routine instance
}
}
2016-11-17 22:10:31 +01:00
// Unable to find such routine
2021-02-03 16:50:39 +01:00
STHROWF("Unable to find a routine with tag ({})", tag.mPtr);
2016-11-17 22:10:31 +01:00
// Should not reach this point but if it did, we have to return something
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warray-bounds"
#endif
return s_Instances[SQMOD_MAX_ROUTINES].mInst; // Intentional Buffer overflow!
#ifdef __clang__
#pragma clang diagnostic pop
#endif
}
/* --------------------------------------------------------------------------------------------
* Check if a routine with a certain tag exists.
*/
static bool IsWithTag(StackStrF & tag);
/* --------------------------------------------------------------------------------------------
* Check if a routine with a certain tag exists.
*/
static bool TerminateWithTag(StackStrF & tag);
2016-02-21 08:25:46 +01:00
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* Process all active routines and update elapsed time.
*/
2016-11-17 22:10:31 +01:00
static void Process();
2016-02-21 08:25:46 +01:00
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* Initialize all resources and prepare for startup.
*/
2016-11-17 22:10:31 +01:00
static void Initialize();
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* Release all resources and prepare for shutdown.
*/
2016-11-17 22:10:31 +01:00
static void Deinitialize();
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* Create a routine with the specified parameters.
*/
2016-11-17 22:10:31 +01:00
static SQInteger Create(HSQUIRRELVM vm);
2016-11-17 22:10:31 +01:00
protected:
2016-02-21 08:25:46 +01:00
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* See whether this routine is valid otherwise throw an exception.
2016-02-21 08:25:46 +01:00
*/
2016-11-17 22:10:31 +01:00
void Validate() const
{
if (m_Slot >= SQMOD_MAX_ROUTINES)
{
STHROWF("This instance does not reference a valid routine");
}
}
2016-02-21 08:25:46 +01:00
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* See whether this routine is valid otherwise throw an exception.
2016-02-21 08:25:46 +01:00
*/
SQMOD_NODISCARD Instance & GetValid() const
2016-11-17 22:10:31 +01:00
{
if (m_Slot >= SQMOD_MAX_ROUTINES)
{
STHROWF("This instance does not reference a valid routine");
}
// We know it's valid so let's return it
return s_Instances[m_Slot];
}
2016-02-21 08:25:46 +01:00
2016-11-17 22:10:31 +01:00
public:
2016-02-21 08:25:46 +01:00
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* Used by the script engine to convert an instance of this type to a string.
2016-02-21 08:25:46 +01:00
*/
SQMOD_NODISCARD const String & ToString() const
2016-11-17 22:10:31 +01:00
{
return (m_Slot >= SQMOD_MAX_ROUTINES) ? NullString() : s_Instances[m_Slot].mTag;
}
2016-02-21 08:25:46 +01:00
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* Terminate the routine.
2016-02-21 08:25:46 +01:00
*/
2016-11-17 22:10:31 +01:00
void Terminate()
{
GetValid().Terminate();
2020-04-30 22:27:24 +02:00
s_Intervals[m_Slot] = 0;
m_Slot = SQMOD_MAX_ROUTINES;
2016-11-17 22:10:31 +01:00
}
2016-02-21 08:25:46 +01:00
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* Retrieve the associated user tag.
2016-02-21 08:25:46 +01:00
*/
SQMOD_NODISCARD const String & GetTag() const
2016-11-17 22:10:31 +01:00
{
return GetValid().mTag;
}
2016-02-21 08:25:46 +01:00
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* Modify the associated user tag.
2016-02-21 08:25:46 +01:00
*/
void SetTag(StackStrF & tag)
2016-11-17 22:10:31 +01:00
{
2020-03-22 05:53:04 +01:00
GetValid().mTag.assign(tag.mPtr, static_cast< size_t >(ClampMin(tag.mLen, 0)));
2016-11-17 22:10:31 +01:00
}
2016-02-21 08:25:46 +01:00
/* --------------------------------------------------------------------------------------------
* Modify the associated user tag.
*/
Routine & ApplyTag(StackStrF & tag)
{
SetTag(tag);
return *this;
}
2016-02-21 08:25:46 +01:00
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* Retrieve the environment object.
2016-02-21 08:25:46 +01:00
*/
SQMOD_NODISCARD const LightObj & GetEnv() const
2016-11-17 22:10:31 +01:00
{
return GetValid().mEnv;
}
2016-02-21 08:25:46 +01:00
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* Modify the environment object.
2016-02-21 08:25:46 +01:00
*/
2016-11-17 22:10:31 +01:00
void SetEnv(const LightObj & env)
{
GetValid().mEnv = env.IsNull() ? LightObj(RootTable{}.GetObj()) : env;
2016-11-17 22:10:31 +01:00
}
2016-02-21 08:25:46 +01:00
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* Retrieve the function object.
2016-02-21 08:25:46 +01:00
*/
SQMOD_NODISCARD const LightObj & GetFunc() const
2016-11-17 22:10:31 +01:00
{
return GetValid().mFunc;
}
2016-02-21 08:25:46 +01:00
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* Modify the function object.
2016-02-21 08:25:46 +01:00
*/
2016-11-17 22:10:31 +01:00
void SetFunc(const Function & func)
{
// Validate the specified
if (!sq_isclosure(func.GetFunc()) && !sq_isnativeclosure(func.GetFunc()))
{
2021-02-03 16:50:39 +01:00
STHROWF("Invalid callback type {}", SqTypeName(GetValid().mFunc.GetType()));
2016-11-17 22:10:31 +01:00
}
// Store the function without the environment
GetValid().mFunc = LightObj(func.GetFunc());
}
2016-02-21 08:25:46 +01:00
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* Retrieve the arbitrary user data object.
2016-02-21 08:25:46 +01:00
*/
SQMOD_NODISCARD const LightObj & GetData() const
2016-11-17 22:10:31 +01:00
{
return GetValid().mData;
}
2016-02-21 08:25:46 +01:00
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* Modify the arbitrary user data object.
2016-02-21 08:25:46 +01:00
*/
2016-11-17 22:10:31 +01:00
void SetData(const LightObj & data)
{
GetValid().mData = data;
}
/* --------------------------------------------------------------------------------------------
* Modify the arbitrary user data object.
*/
Routine & ApplyData(const LightObj & data)
{
SetData(data);
return *this;
}
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* Retrieve the execution interval.
*/
SQMOD_NODISCARD SQInteger GetInterval() const
2016-11-17 22:10:31 +01:00
{
return ConvTo< SQInteger >::From(GetValid().mInterval);
}
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* Modify the execution interval.
*/
2016-11-17 22:10:31 +01:00
void SetInterval(SQInteger itr)
{
GetValid().mInterval = ClampMin(ConvTo< Interval >::From(itr), static_cast< Interval >(0));
}
/* --------------------------------------------------------------------------------------------
* Modify the execution interval.
*/
Routine & ApplyInterval(SQInteger itr)
{
SetInterval(itr);
return *this;
}
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* Retrieve the number of iterations.
*/
SQMOD_NODISCARD SQInteger GetIterations() const
2016-11-17 22:10:31 +01:00
{
return ConvTo< SQInteger >::From(GetValid().mIterations);
}
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* Modify the number of iterations.
*/
2016-11-17 22:10:31 +01:00
void SetIterations(SQInteger itr)
{
GetValid().mIterations = ConvTo< Iterator >::From(itr);
}
/* --------------------------------------------------------------------------------------------
* Modify the number of iterations.
*/
Routine & ApplyIterations(SQInteger itr)
{
SetIterations(itr);
return *this;
}
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* See whether the routine is suspended.
*/
SQMOD_NODISCARD bool GetSuspended() const
2016-11-17 22:10:31 +01:00
{
return GetValid().mSuspended;
}
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* Set whether the routine should be suspended.
*/
2016-11-17 22:10:31 +01:00
void SetSuspended(bool toggle)
{
GetValid().mSuspended = toggle;
}
/* --------------------------------------------------------------------------------------------
* Set whether the routine should be suspended.
*/
Routine & ApplySuspended(bool toggle)
{
SetSuspended(toggle);
return *this;
}
/* --------------------------------------------------------------------------------------------
* See whether the routine is quite.
*/
SQMOD_NODISCARD bool GetQuiet() const
{
return GetValid().mQuiet;
}
/* --------------------------------------------------------------------------------------------
* Set whether the routine should be quiet.
*/
void SetQuiet(bool toggle)
{
GetValid().mQuiet = toggle;
}
/* --------------------------------------------------------------------------------------------
* Set whether the routine should be quiet.
*/
Routine & ApplyQuiet(bool toggle)
{
SetQuiet(toggle);
return *this;
}
/* --------------------------------------------------------------------------------------------
* See whether the routine endures.
*/
SQMOD_NODISCARD bool GetEndure() const
{
return GetValid().mEndure;
}
/* --------------------------------------------------------------------------------------------
* Set whether the routine should endure.
*/
void SetEndure(bool toggle)
{
GetValid().mEndure = toggle;
}
/* --------------------------------------------------------------------------------------------
* Set whether the routine should endure.
*/
Routine & ApplyEndure(bool toggle)
{
SetEndure(toggle);
return *this;
}
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* Retrieve the number of arguments to be forwarded.
*/
SQMOD_NODISCARD SQInteger GetArguments() const
2016-11-17 22:10:31 +01:00
{
return ConvTo< SQInteger >::From(GetValid().mArgc);
}
/* --------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
* Retrieve a certain argument.
*/
SQMOD_NODISCARD const Argument & GetArgument(SQInteger arg) const
2016-11-17 22:10:31 +01:00
{
// Cast the index to the proper value
uint8_t idx = ConvTo< uint8_t >::From(arg);
2016-11-17 22:10:31 +01:00
// Validate the specified index
if (idx >= 14)
{
2021-02-03 16:50:39 +01:00
STHROWF("The specified index is out of range: {} >= {}", idx, 14);
2016-11-17 22:10:31 +01:00
}
// Return the requested argument
return GetValid().mArgv[idx];
}
/* --------------------------------------------------------------------------------------------
* Release the environment object and default to self.
*/
void DropEnv()
{
GetValid().mEnv.Release();
}
/* --------------------------------------------------------------------------------------------
* See if error reporting is enabled for all newly created routines.
*/
static bool GetSilenced()
{
return s_Silenced;
}
/* --------------------------------------------------------------------------------------------
* Set if error reporting should be enabled for all newly created routines.
*/
static void SetSilenced(bool toggle)
{
s_Silenced = toggle;
}
2016-02-21 08:25:46 +01:00
};
} // Namespace:: SqMod