Add support for building and running multiple instances of the same script.

git-svn-id: http://yate.null.ro/svn/yate/trunk@6542 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
oana 2022-02-09 10:59:28 +00:00
parent 169a579ea8
commit 71206f078c
5 changed files with 369 additions and 82 deletions

View File

@ -44,6 +44,16 @@
;keep_old_on_fail=no ;keep_old_on_fail=no
[instances]
; Build multiple instances of specified scripts
; Each line has to be of the form
; name=number of instances
; The name must correspond with a [scripts] section entry
; Default value is one instance
; Examples:
; faxes=3
[scripts] [scripts]
; Add one entry in this section for each script that is to be loaded on Yate startup ; Add one entry in this section for each script that is to be loaded on Yate startup
; Each line has to be on the form: ; Each line has to be on the form:

View File

@ -35,7 +35,7 @@ class JsContext : public JsObject, public ScriptMutex
{ {
YCLASS(JsContext,JsObject) YCLASS(JsContext,JsObject)
public: public:
inline JsContext() inline JsContext(unsigned int instIdx = 0, unsigned int maxInst = 1)
: JsObject("Context",0), ScriptMutex(true,"JsContext"), : JsObject("Context",0), ScriptMutex(true,"JsContext"),
m_trackObjs(0), m_trackObjsMtx(false,"JsObjTrack") m_trackObjs(0), m_trackObjsMtx(false,"JsObjTrack")
{ {
@ -44,6 +44,7 @@ public:
params().addParam(new ExpFunction("parseInt")); params().addParam(new ExpFunction("parseInt"));
params().addParam(new ExpOperation(ExpOperation::nonInteger(),"NaN")); params().addParam(new ExpOperation(ExpOperation::nonInteger(),"NaN"));
m_objTrack = !!m_trackObjs; m_objTrack = !!m_trackObjs;
setInstance(instIdx,maxInst);
DDebug(DebugAll,"JsContext::JsContext() [%p]",this); DDebug(DebugAll,"JsContext::JsContext() [%p]",this);
} }
virtual void destroyed(); virtual void destroyed();
@ -3217,7 +3218,11 @@ void JsRunner::traceCheck(const char* title)
traceStart(title); traceStart(title);
return; return;
} }
traceStart(title,ns->c_str());
String filename = ns->c_str();
if (context()->instanceIndex())
filename << "_" << context()->instanceIndex();
traceStart(title,filename);
if (m_stats) { if (m_stats) {
m_stats->ref(); m_stats->ref();
context()->params().setParam(new ExpWrapper(m_stats,s_tracingObj)); context()->params().setParam(new ExpWrapper(m_stats,s_tracingObj));
@ -3844,18 +3849,19 @@ void JsParser::adjustPath(String& script, bool extraInc) const
} }
// Create Javascript context // Create Javascript context
ScriptContext* JsParser::createContext() const ScriptContext* JsParser::createContext(unsigned int instIdx, unsigned int maxInst) const
{ {
return new JsContext; return new JsContext(instIdx,maxInst);
} }
ScriptRun* JsParser::createRunner(ScriptCode* code, ScriptContext* context, const char* title) const ScriptRun* JsParser::createRunner(ScriptCode* code, ScriptContext* context, const char* title,
unsigned int instIdx, unsigned int maxInst) const
{ {
if (!code) if (!code)
return 0; return 0;
ScriptContext* ctxt = 0; ScriptContext* ctxt = 0;
if (!context) if (!context)
context = ctxt = createContext(); context = ctxt = createContext(instIdx,maxInst);
ScriptRun* runner = new JsRunner(code,context,title); ScriptRun* runner = new JsRunner(code,context,title);
TelEngine::destruct(ctxt); TelEngine::destruct(ctxt);
return runner; return runner;

View File

@ -77,18 +77,19 @@ void ScriptParser::setCode(ScriptCode* code)
TelEngine::destruct(tmp); TelEngine::destruct(tmp);
} }
ScriptContext* ScriptParser::createContext() const ScriptContext* ScriptParser::createContext(unsigned int instIdx, unsigned int maxInst) const
{ {
return new BasicContext; return new BasicContext;
} }
ScriptRun* ScriptParser::createRunner(ScriptCode* code, ScriptContext* context, const char* title) const ScriptRun* ScriptParser::createRunner(ScriptCode* code, ScriptContext* context, const char* title,
unsigned int instIdx, unsigned int maxInst) const
{ {
if (!code) if (!code)
return 0; return 0;
ScriptContext* ctxt = 0; ScriptContext* ctxt = 0;
if (!context) if (!context)
context = ctxt = createContext(); context = ctxt = createContext(instIdx,maxInst);
ScriptRun* runner = new ScriptRun(code,context); ScriptRun* runner = new ScriptRun(code,context);
TelEngine::destruct(ctxt); TelEngine::destruct(ctxt);
return runner; return runner;

View File

@ -1603,7 +1603,7 @@ public:
* @param name Name of the context * @param name Name of the context
*/ */
inline explicit ScriptContext(const char* name = 0) inline explicit ScriptContext(const char* name = 0)
: m_params(name) : m_params(name), m_instIdx(0), m_instCount(1)
{ } { }
/** /**
@ -1776,8 +1776,22 @@ public:
virtual ObjList* countAllocations() virtual ObjList* countAllocations()
{ return 0; } { return 0; }
virtual void setInstance(unsigned int idx, unsigned int count)
{
m_instIdx = idx;
m_instCount = count;
}
virtual unsigned int instanceIndex() const
{ return m_instIdx; }
virtual unsigned int instanceCount() const
{ return m_instCount; }
private: private:
NamedList m_params; NamedList m_params;
unsigned int m_instIdx; // instance index
unsigned int m_instCount; // total number of instances
}; };
/** /**
@ -2155,7 +2169,7 @@ public:
* Create a context adequate for the parsed code * Create a context adequate for the parsed code
* @return A new script context * @return A new script context
*/ */
virtual ScriptContext* createContext() const; virtual ScriptContext* createContext(unsigned int instIdx = 0, unsigned int maxInst = 1) const;
/** /**
* Create a runner adequate for a block of parsed code * Create a runner adequate for a block of parsed code
@ -2164,7 +2178,8 @@ public:
* @param title An optional name for the runner * @param title An optional name for the runner
* @return A new script runner, NULL if code is NULL * @return A new script runner, NULL if code is NULL
*/ */
virtual ScriptRun* createRunner(ScriptCode* code, ScriptContext* context = 0, const char* title = 0) const; virtual ScriptRun* createRunner(ScriptCode* code, ScriptContext* context = 0, const char* title = 0,
unsigned int instIdx = 0, unsigned int maxInst = 1) const;
/** /**
* Create a runner adequate for the parsed code * Create a runner adequate for the parsed code
@ -2172,8 +2187,9 @@ public:
* @param title An optional name for the runner * @param title An optional name for the runner
* @return A new script runner, NULL if code is not yet parsed * @return A new script runner, NULL if code is not yet parsed
*/ */
inline ScriptRun* createRunner(ScriptContext* context = 0, const char* title = 0) const inline ScriptRun* createRunner(ScriptContext* context = 0, const char* title = 0,
{ return createRunner(code(),context,title); } unsigned int instIdx = 0, unsigned int maxInst = 1) const
{ return createRunner(code(),context,title,instIdx,maxInst); }
/** /**
* Check if a script has a certain function or method * Check if a script has a certain function or method
@ -3148,7 +3164,7 @@ public:
* Create a context adequate for Javascript code * Create a context adequate for Javascript code
* @return A new Javascript context * @return A new Javascript context
*/ */
virtual ScriptContext* createContext() const; virtual ScriptContext* createContext(unsigned int instIdx = 0, unsigned int maxInst = 1) const;
/** /**
* Create a runner adequate for a block of parsed Javascript code * Create a runner adequate for a block of parsed Javascript code
@ -3157,16 +3173,20 @@ public:
* @param title An optional name for the runner * @param title An optional name for the runner
* @return A new Javascript runner, NULL if code is NULL * @return A new Javascript runner, NULL if code is NULL
*/ */
virtual ScriptRun* createRunner(ScriptCode* code, ScriptContext* context = 0, const char* title = 0) const; virtual ScriptRun* createRunner(ScriptCode* code, ScriptContext* context = 0, const char* title = 0,
unsigned int instIdx = 0, unsigned int maxInst = 1) const;
/** /**
* Create a runner adequate for the parsed Javascript code * Create a runner adequate for the parsed Javascript code
* @param context Javascript context, an empty one will be allocated if NULL * @param context Javascript context, an empty one will be allocated if NULL
* @param title An optional name for the runner * @param title An optional name for the runner
* @param instIdx Javascript context instance
* @param maxInst Number of context instances
* @return A new Javascript runner, NULL if code is not yet parsed * @return A new Javascript runner, NULL if code is not yet parsed
*/ */
inline ScriptRun* createRunner(ScriptContext* context = 0, const char* title = 0) const inline ScriptRun* createRunner(ScriptContext* context = 0, const char* title = 0,
{ return createRunner(code(),context,title); } unsigned int instIdx = 0, unsigned int maxInst = 1) const
{ return createRunner(code(),context,title,instIdx,maxInst); }
/** /**
* Check if a script has a certain function or method * Check if a script has a certain function or method

View File

@ -182,26 +182,65 @@ private:
RefPointer<JsMessage> m_message; RefPointer<JsMessage> m_message;
}; };
class JsGlobal;
class JsGlobalInstance : public RefObject
{
public:
JsGlobalInstance(JsGlobal* owner, unsigned int index);
~JsGlobalInstance();
unsigned int runMain();
inline ScriptContext* context()
{ return m_context; }
const String& toString() const
{ return m_name; }
private:
JsGlobal* m_owner;
RefPointer<ScriptContext> m_context;
String m_name;
unsigned int m_instance;
};
class JsGlobal : public NamedString class JsGlobal : public NamedString
{ {
public: public:
JsGlobal(const char* scriptName, const char* fileName, bool relPath = true, bool fromCfg = true); JsGlobal(const char* scriptName, const char* fileName, bool relPath = true,
bool fromCfg = true, unsigned int instances = 1);
virtual ~JsGlobal(); virtual ~JsGlobal();
bool load(); bool load();
bool fileChanged(const char* fileName) const; bool fileChanged(const char* fileName) const;
bool updateInstances(unsigned int instances);
inline JsParser& parser() inline JsParser& parser()
{ return m_jsCode; } { return m_jsCode; }
inline ScriptContext* context()
{ return m_context; }
inline const String& fileName() inline const String& fileName()
{ return m_file; } { return m_file; }
inline unsigned int instances() const
{ return m_instanceCount; }
inline JsGlobalInstance* getInstance(unsigned int idx) const
{
String str = name();
if (idx)
str << "/" << idx;
JsGlobalInstance* inst = static_cast<JsGlobalInstance*>(m_instances[str]);
if (inst && inst->ref())
return inst;
return 0;
}
inline JsGlobalInstance* getInstance(const String& name) const
{
JsGlobalInstance* inst = static_cast<JsGlobalInstance*>(m_instances[name]);
if (inst && inst->ref())
return inst;
return 0;
}
bool runMain(); bool runMain();
static void markUnused(); static void markUnused();
static void freeUnused(); static void freeUnused();
static void reloadDynamic(); static void reloadDynamic();
static bool initScript(const String& scriptName, const String& fileName, bool relPath = true, bool fromCfg = true); static bool initScript(const String& scriptName, const String& fileName,
bool relPath = true, bool fromCfg = true, unsigned int instances = 0);
static bool reloadScript(const String& scriptName); static bool reloadScript(const String& scriptName);
static void loadScripts(const NamedList* sect); static void loadScripts(const NamedList* sect, const NamedList* instSect);
inline static ObjList& globals() inline static ObjList& globals()
{ return s_globals; } { return s_globals; }
inline static void unloadAll() inline static void unloadAll()
@ -212,13 +251,14 @@ public:
private: private:
static bool buildNewScript(Lock& lck, ObjList* old, const String& scriptName, static bool buildNewScript(Lock& lck, ObjList* old, const String& scriptName,
const String& fileName, bool relPath, bool fromCfg, bool fromInit = false); const String& fileName, bool relPath, bool fromCfg, bool fromInit = false, unsigned int instances = 0);
JsParser m_jsCode; JsParser m_jsCode;
RefPointer<ScriptContext> m_context;
bool m_inUse; bool m_inUse;
bool m_confLoaded; bool m_confLoaded;
String m_file; String m_file;
unsigned int m_instanceCount;
ObjList m_instances;
static ObjList s_globals; static ObjList s_globals;
}; };
@ -529,6 +569,8 @@ public:
params().addParam(new ExpFunction("htoa")); params().addParam(new ExpFunction("htoa"));
params().addParam(new ExpFunction("btoh")); params().addParam(new ExpFunction("btoh"));
params().addParam(new ExpFunction("htob")); params().addParam(new ExpFunction("htob"));
params().addParam(new ExpFunction("instanceIndex"));
params().addParam(new ExpFunction("instanceCount"));
addConstructor(params(),"Semaphore",new JsSemaphore(mtx)); addConstructor(params(),"Semaphore",new JsSemaphore(mtx));
addConstructor(params(),"HashList",new JsHashList(mtx)); addConstructor(params(),"HashList",new JsHashList(mtx));
addConstructor(params(),"URI",new JsURI(mtx)); addConstructor(params(),"URI",new JsURI(mtx));
@ -1249,6 +1291,24 @@ static int counterSort(GenObject* obj1, GenObject* obj2, void* context)
return c1 < c2 ? 1 : (c1 > c2 ? -1 : 0); return c1 < c2 ? 1 : (c1 > c2 ? -1 : 0);
} }
// Sort and dump a list of object counters
static void dumpAllocations(String& out, ObjList* counters, unsigned int count, ScriptCode* code)
{
if (!(counters && code))
return;
counters->sort(counterSort);
unsigned int i = 0;
for (ObjList* o = counters->skipNull(); o && i < count; o = o->skipNext(), i++) {
NamedCounter* c = static_cast<NamedCounter*>(o->get());
uint64_t line = c->toString().toUInt64();
String fn;
unsigned int fl = 0;
code->getFileLine(line,fn,fl,false);
out << "\r\n" << fn << ":" << fl << " " << c->count();
}
}
// Obtain a string with top object allocations from context ctx // Obtain a string with top object allocations from context ctx
static bool evalCtxtAllocations(String& retVal, unsigned int count, ScriptContext* ctx, static bool evalCtxtAllocations(String& retVal, unsigned int count, ScriptContext* ctx,
ScriptCode* code, const String& scrName) ScriptCode* code, const String& scrName)
@ -1262,17 +1322,8 @@ static bool evalCtxtAllocations(String& retVal, unsigned int count, ScriptContex
retVal << "Script '" << scrName << "' has no active object tracking\r\n"; retVal << "Script '" << scrName << "' has no active object tracking\r\n";
return true; return true;
} }
objCounters->sort(counterSort);
String tmp; String tmp;
unsigned int i = 0; dumpAllocations(tmp,objCounters,count,code);
for (ObjList* o = objCounters->skipNull(); o && i < count; o = o->skipNext(), i++) {
NamedCounter* c = static_cast<NamedCounter*>(o->get());
uint64_t line = c->toString().toUInt64();
String fn;
unsigned int fl = 0;
code->getFileLine(line,fn,fl,false);
tmp << "\r\n" << fn << ":" << fl << " " << c->count();
}
if (!tmp) if (!tmp)
retVal << "Script '" << scrName << "' has no active object tracking counters\r\n"; retVal << "Script '" << scrName << "' has no active object tracking counters\r\n";
else else
@ -1281,6 +1332,39 @@ static bool evalCtxtAllocations(String& retVal, unsigned int count, ScriptContex
return true; return true;
} }
// Obtain a string with top object allocations from all script instances contexts
static bool evalInstanceAllocations(String& retVal, unsigned int count, ObjList& list,
ScriptCode* code, const String& scrName)
{
if (!code) {
retVal << "Script '" << scrName << "' has no associated code\r\n";
return true;
}
ObjList objCounters;
for (ObjList* o = list.skipNull(); o; o = o->skipNext()) {
ObjList* l = static_cast<ObjList*>(o->get());
for (ObjList* j = l->skipNull(); j; j = j->skipNext()) {
NamedCounter* nInt = static_cast<NamedCounter*>(j->get());
NamedCounter* total = static_cast<NamedCounter*>(objCounters[nInt->toString()]);
if (total)
total->add(nInt->count());
else {
j->set(0,false);
objCounters.insert(nInt);
}
}
o->set(0);
}
String tmp;
dumpAllocations(tmp,&objCounters,count,code);
if (!tmp)
retVal << "Script '" << scrName << "' has no active object tracking counters\r\n";
else
retVal << "Top " << count << " object allocations for '" << scrName <<"':" << tmp << "\r\n";
return true;
}
// Utility: return a list of parameters to be used for replaceParams // Utility: return a list of parameters to be used for replaceParams
static inline const NamedList* getReplaceParams(GenObject* gen) static inline const NamedList* getReplaceParams(GenObject* gen)
{ {
@ -2350,6 +2434,18 @@ bool JsEngine::runNative(ObjList& stack, const ExpOperation& oper, GenObject* co
else else
ExpEvaluator::pushOne(stack,new ExpOperation(false)); ExpEvaluator::pushOne(stack,new ExpOperation(false));
} }
else if (oper.name() == YSTRING("instanceIndex")) {
ScriptRun* runner = YOBJECT(ScriptRun,context);
if (!(runner && runner->context()))
return false;
ExpEvaluator::pushOne(stack,new ExpOperation((int64_t)runner->context()->instanceIndex()));
}
else if (oper.name() == YSTRING("instanceCount")) {
ScriptRun* runner = YOBJECT(ScriptRun,context);
if (!(runner && runner->context()))
return false;
ExpEvaluator::pushOne(stack,new ExpOperation((int64_t)runner->context()->instanceCount()));
}
else else
return JsObject::runNative(stack,oper,context); return JsObject::runNative(stack,oper,context);
return true; return true;
@ -6121,14 +6217,56 @@ void JsAssist::msgPostExecute(const Message& msg, bool handled)
runFunction("onPostExecute",const_cast<Message&>(msg),&handled); runFunction("onPostExecute",const_cast<Message&>(msg),&handled);
} }
JsGlobalInstance::JsGlobalInstance(JsGlobal* owner, unsigned int index)
: m_owner(owner), m_instance(index)
{
m_name << owner->toString();
if (index)
m_name << "/" << index;
Debug(&__plugin,DebugInfo,"JsGlobalInstance::JsGlobalInstance(%p,%u) Created script instance '%s' [%p]",
m_owner,index,m_name.c_str(),this);
}
JsGlobalInstance::~JsGlobalInstance()
{
Debug(&__plugin,DebugInfo,"JsGlobalInstance::~JsGlobalInstance() '%s' destroyed [%p]",m_name.c_str(),this);
if (m_owner->parser().callable("onUnload")) {
ScriptRun* runner = m_owner->parser().createRunner(m_context,NATIVE_TITLE);
if (runner) {
ObjList args;
runner->call("onUnload",args);
TelEngine::destruct(runner);
}
}
if (m_context) {
Lock mylock(m_context->mutex());
m_context->params().clearParams();
}
}
unsigned int JsGlobalInstance::runMain()
{
DDebug(&__plugin,DebugInfo,"JsGlobalInstance::runMain() Start instance %s",toString().c_str());
ScriptRun* runner = m_owner->parser().createRunner(m_context,0,m_instance,m_owner->instances());
if (!runner)
return ScriptRun::Failed;
if (!m_context)
m_context = runner->context();
m_context->trackObjs(s_trackCreation);
contextInit(runner,toString());
ScriptRun::Status st = runner->run();
TelEngine::destruct(runner);
return st;
}
ObjList JsGlobal::s_globals; ObjList JsGlobal::s_globals;
Mutex JsGlobal::s_mutex(false,"JsGlobal"); Mutex JsGlobal::s_mutex(false,"JsGlobal");
bool JsGlobal::s_keepOldOnFail = false; bool JsGlobal::s_keepOldOnFail = false;
JsGlobal::JsGlobal(const char* scriptName, const char* fileName, bool relPath, bool fromCfg) JsGlobal::JsGlobal(const char* scriptName, const char* fileName, bool relPath,
bool fromCfg, unsigned int instances)
: NamedString(scriptName,fileName), : NamedString(scriptName,fileName),
m_inUse(true), m_confLoaded(fromCfg), m_file(fileName) m_inUse(true), m_confLoaded(fromCfg), m_file(fileName), m_instanceCount(instances)
{ {
m_jsCode.basePath(s_basePath,s_libsPath); m_jsCode.basePath(s_basePath,s_libsPath);
if (relPath) if (relPath)
@ -6141,18 +6279,7 @@ JsGlobal::JsGlobal(const char* scriptName, const char* fileName, bool relPath, b
JsGlobal::~JsGlobal() JsGlobal::~JsGlobal()
{ {
DDebug(&__plugin,DebugAll,"Unloading global Javascript '%s'",name().c_str()); DDebug(&__plugin,DebugAll,"Unloading global Javascript '%s'",name().c_str());
if (m_jsCode.callable("onUnload")) { m_instances.clear();
ScriptRun* runner = m_jsCode.createRunner(m_context,NATIVE_TITLE);
if (runner) {
ObjList args;
runner->call("onUnload",args);
TelEngine::destruct(runner);
}
}
if (m_context) {
Lock mylock(m_context->mutex());
m_context->params().clearParams();
}
} }
bool JsGlobal::load() bool JsGlobal::load()
@ -6172,6 +6299,21 @@ bool JsGlobal::fileChanged(const char* fileName) const
return m_jsCode.scriptChanged(fileName,s_basePath,s_libsPath); return m_jsCode.scriptChanged(fileName,s_basePath,s_libsPath);
} }
bool JsGlobal::updateInstances(unsigned int instances)
{
// 0 means that it was called from some place where config was not read, so no change
// or no change in number of instances
if (!instances || instances == m_instanceCount)
return true;
// we already now that current instance is different from what is requested
// so if instances is 1 => m_instance count > 1, m_instanceCount == 1 => instance = 1
// so we need to completely reload the script
if (instances == 1 || m_instanceCount == 1)
return false;
m_instanceCount = instances;
return runMain();
}
void JsGlobal::markUnused() void JsGlobal::markUnused()
{ {
for (ObjList* o = s_globals.skipNull(); o; o = o->skipNext()) { for (ObjList* o = s_globals.skipNull(); o; o = o->skipNext()) {
@ -6207,12 +6349,13 @@ void JsGlobal::reloadDynamic()
} }
} }
bool JsGlobal::initScript(const String& scriptName, const String& fileName, bool relPath, bool fromCfg) bool JsGlobal::initScript(const String& scriptName, const String& fileName, bool relPath,
bool fromCfg, unsigned int instances)
{ {
if (fileName.null()) if (fileName.null())
return false; return false;
DDebug(&__plugin,DebugInfo,"Initialize %s script '%s' from %s file '%s'",(fromCfg ? "configured" : "dynamically loaded"), DDebug(&__plugin,DebugInfo,"Initialize %s script '%s' from %s file '%s' instances=%u",(fromCfg ? "configured" : "dynamically loaded"),
scriptName.c_str(),(relPath ? "relative" : "absolute"),fileName.c_str()); scriptName.c_str(),(relPath ? "relative" : "absolute"),fileName.c_str(),instances);
Lock mylock(JsGlobal::s_mutex); Lock mylock(JsGlobal::s_mutex);
ObjList* o = s_globals.find(scriptName); ObjList* o = s_globals.find(scriptName);
if (o) { if (o) {
@ -6224,12 +6367,16 @@ bool JsGlobal::initScript(const String& scriptName, const String& fileName, bool
return false; return false;
} }
if (!script->fileChanged(fileName)) { if (!script->fileChanged(fileName)) {
unsigned int ret = script->updateInstances(instances);
script->m_inUse = true; script->m_inUse = true;
script->m_confLoaded = fromCfg; script->m_confLoaded = fromCfg;
return true; // if positive, we can return. otherwise, it's a transition
// from multiple instances to one or viceversa and we reload the whole script
if (ret)
return true;
} }
} }
return buildNewScript(mylock,o,scriptName,fileName,relPath,fromCfg,true); return buildNewScript(mylock,o,scriptName,fileName,relPath,fromCfg,true,instances);
} }
bool JsGlobal::reloadScript(const String& scriptName) bool JsGlobal::reloadScript(const String& scriptName)
@ -6245,7 +6392,7 @@ bool JsGlobal::reloadScript(const String& scriptName)
return fileName && buildNewScript(mylock,o,scriptName,fileName,false,script->m_confLoaded); return fileName && buildNewScript(mylock,o,scriptName,fileName,false,script->m_confLoaded);
} }
void JsGlobal::loadScripts(const NamedList* sect) void JsGlobal::loadScripts(const NamedList* sect, const NamedList* instSect)
{ {
if (!sect) if (!sect)
return; return;
@ -6256,33 +6403,60 @@ void JsGlobal::loadScripts(const NamedList* sect)
continue; continue;
String tmp = *n; String tmp = *n;
Engine::runParams().replaceParams(tmp); Engine::runParams().replaceParams(tmp);
JsGlobal::initScript(n->name(),tmp); JsGlobal::initScript(n->name(),tmp,true,true,instSect ? instSect->getIntValue(n->name(),0,0) : 0);
} }
} }
bool JsGlobal::runMain() bool JsGlobal::runMain()
{ {
ScriptRun* runner = m_jsCode.createRunner(m_context); DDebug(&__plugin,DebugInfo,"JsGlobal::runMain() Load and run %u instances, current number of instances:%u",
if (!runner) m_instanceCount,m_instances.count());
return false; if (m_instanceCount <= 1) {
if (!m_context) JsGlobalInstance* inst = new JsGlobalInstance(this,0);
m_context = runner->context(); if(ScriptRun::Succeeded != inst->runMain())
m_context->trackObjs(s_trackCreation); return false;
contextInit(runner,name()); m_instances.append(inst);
ScriptRun::Status st = runner->run(); }
TelEngine::destruct(runner); else {
return (ScriptRun::Succeeded == st); unsigned int lCount = m_instances.count();
// add instances if m_instancesCount is increased
for (unsigned int i = 0; i < m_instanceCount; i++) {
JsGlobalInstance* inst = getInstance(i + 1);
if (inst) {
TelEngine::destruct(inst);
continue;
}
inst = new JsGlobalInstance(this,i + 1);
if(ScriptRun::Succeeded != inst->runMain())
return false;
m_instances.append(inst);
}
// remove instances if m_instances was decreased
for (unsigned int i = m_instanceCount; i < lCount; i++) {
JsGlobalInstance* inst = getInstance(i + 1);
if (!inst)
continue;
m_instances.remove(inst);
TelEngine::destruct(inst);
}
}
return true;
} }
bool JsGlobal::buildNewScript(Lock& lck, ObjList* old, const String& scriptName, bool JsGlobal::buildNewScript(Lock& lck, ObjList* old, const String& scriptName,
const String& fileName, bool relPath, bool fromCfg, bool fromInit) const String& fileName, bool relPath, bool fromCfg, bool fromInit, unsigned int instances)
{ {
bool objCount = s_trackObj && getObjCounting(); bool objCount = s_trackObj && getObjCounting();
NamedCounter* saved = 0; NamedCounter* saved = 0;
if (objCount) if (objCount)
saved = Thread::setCurrentObjCounter(getObjCounter("js:" + scriptName,true)); saved = Thread::setCurrentObjCounter(getObjCounter("js:" + scriptName,true));
JsGlobal* oldScript = old ? static_cast<JsGlobal*>(old->get()) : 0; JsGlobal* oldScript = old ? static_cast<JsGlobal*>(old->get()) : 0;
JsGlobal* script = new JsGlobal(scriptName,fileName,relPath,fromCfg); if (0 == instances) // keep number of instances if none is given
instances = oldScript ? oldScript->instances() : 1;
JsGlobal* script = new JsGlobal(scriptName,fileName,relPath,fromCfg,instances);
bool ok = false; bool ok = false;
if (script->load() || !s_keepOldOnFail || !old) { if (script->load() || !s_keepOldOnFail || !old) {
if (old) if (old)
@ -6374,7 +6548,10 @@ bool JsModule::commandExecute(String& retVal, const String& line)
Lock lck(JsGlobal::s_mutex); Lock lck(JsGlobal::s_mutex);
for (ObjList* o = JsGlobal::globals().skipNull(); o ; o = o->skipNext()) { for (ObjList* o = JsGlobal::globals().skipNull(); o ; o = o->skipNext()) {
JsGlobal* script = static_cast<JsGlobal*>(o->get()); JsGlobal* script = static_cast<JsGlobal*>(o->get());
retVal << script->name() << " = " << *script << "\r\n"; retVal << script->name() << " = " << *script;
if (script->instances() > 1)
retVal << ":" << script->instances();
retVal << "\r\n";
} }
lck.acquire(this); lck.acquire(this);
for (unsigned int i = 0; i < calls().length(); i++) { for (unsigned int i = 0; i < calls().length(); i++) {
@ -6395,12 +6572,18 @@ bool JsModule::commandExecute(String& retVal, const String& line)
cmd.extractTo(" ",scr).trimSpaces(); cmd.extractTo(" ",scr).trimSpaces();
if (scr.null() || cmd.null()) if (scr.null() || cmd.null())
return false; return false;
int pos = scr.find("/");
String baseScr = scr.substr(0,pos);
Lock mylock(JsGlobal::s_mutex);; Lock mylock(JsGlobal::s_mutex);;
JsGlobal* script = static_cast<JsGlobal*>(JsGlobal::globals()[scr]); JsGlobal* script = static_cast<JsGlobal*>(JsGlobal::globals()[baseScr]);
if (script) { if (script) {
RefPointer<ScriptContext> ctxt = script->context(); JsGlobalInstance* inst = script->getInstance(scr);
mylock.drop(); if (inst) {
return evalContext(retVal,cmd,ctxt); RefPointer<ScriptContext> ctxt = inst->context();
mylock.drop();
TelEngine::destruct(inst);
return evalContext(retVal,cmd,ctxt);
}
} }
mylock.acquire(this); mylock.acquire(this);
JsAssist* assist = static_cast<JsAssist*>(calls()[scr]); JsAssist* assist = static_cast<JsAssist*>(calls()[scr]);
@ -6416,7 +6599,34 @@ bool JsModule::commandExecute(String& retVal, const String& line)
if (cmd.startSkip("eval") && cmd.trimSpaces()) if (cmd.startSkip("eval") && cmd.trimSpaces())
return evalContext(retVal,cmd); return evalContext(retVal,cmd);
if (cmd.startSkip("allocations") && cmd.trimSpaces()) { if (cmd.startSkip("allocations instance") && cmd.trimSpaces()) {
String scr;
cmd.extractTo(" ",scr).trimSpaces();
String baseScr = scr.substr(0,scr.find("/"));
unsigned int top = cmd.toInteger(25,0,1,100);
if (scr.null())
return false;
Lock mylock(JsGlobal::s_mutex);;
JsGlobal* script = static_cast<JsGlobal*>(JsGlobal::globals()[baseScr]);
if (script) {
JsGlobalInstance* inst = script->getInstance(scr);
if (inst) {
bool ret = evalCtxtAllocations(retVal,top,inst->context(),script->parser().code(),scr);
TelEngine::destruct(inst);
return ret;
}
}
mylock.acquire(this);
RefPointer<JsAssist> assist = static_cast<JsAssist*>(calls()[scr]);
if (assist) {
mylock.drop();
return assist->evalAllocations(retVal,top);
}
retVal << "Cannot find script context: " << scr << "\n\r";
return true;
}
if (cmd.startSkip("allocations total") && cmd.trimSpaces()) {
String scr; String scr;
cmd.extractTo(" ",scr).trimSpaces(); cmd.extractTo(" ",scr).trimSpaces();
unsigned int top = cmd.toInteger(25,0,1,100); unsigned int top = cmd.toInteger(25,0,1,100);
@ -6424,8 +6634,20 @@ bool JsModule::commandExecute(String& retVal, const String& line)
return false; return false;
Lock mylock(JsGlobal::s_mutex);; Lock mylock(JsGlobal::s_mutex);;
JsGlobal* script = static_cast<JsGlobal*>(JsGlobal::globals()[scr]); JsGlobal* script = static_cast<JsGlobal*>(JsGlobal::globals()[scr]);
if (script) if (script) {
return evalCtxtAllocations(retVal,top,script->context(),script->parser().code(),scr); ObjList list;
for (unsigned int i = 0; i <= script->instances(); i++) {
JsGlobalInstance* inst = script->getInstance(i);
if (inst) {
ObjList* c = inst->context() ? inst->context()->countAllocations() : 0;
if (c)
list.insert(c);
TelEngine::destruct(inst);
}
}
mylock.drop();
return evalInstanceAllocations(retVal,top,list,script->parser().code(),scr);
}
mylock.acquire(this); mylock.acquire(this);
RefPointer<JsAssist> assist = static_cast<JsAssist*>(calls()[scr]); RefPointer<JsAssist> assist = static_cast<JsAssist*>(calls()[scr]);
if (assist) { if (assist) {
@ -6519,8 +6741,15 @@ bool JsModule::commandComplete(Message& msg, const String& partLine, const Strin
Lock lck(JsGlobal::s_mutex); Lock lck(JsGlobal::s_mutex);
for (ObjList* o = JsGlobal::globals().skipNull(); o ; o = o->skipNext()) { for (ObjList* o = JsGlobal::globals().skipNull(); o ; o = o->skipNext()) {
JsGlobal* script = static_cast<JsGlobal*>(o->get()); JsGlobal* script = static_cast<JsGlobal*>(o->get());
if (script->name()) if (script->name()) {
itemComplete(msg.retValue(),s_eval + script->name(),partWord); for (unsigned int i = 0; i <= script->instances(); i++) {
JsGlobalInstance* inst = script->getInstance(i);
if (inst) {
itemComplete(msg.retValue(),s_eval + inst->toString(),partWord);
TelEngine::destruct(inst);
}
}
}
} }
lck.acquire(this); lck.acquire(this);
for (unsigned int i = 0; i < calls().length(); i++) { for (unsigned int i = 0; i < calls().length(); i++) {
@ -6536,7 +6765,7 @@ bool JsModule::commandComplete(Message& msg, const String& partLine, const Strin
itemComplete(msg.retValue(),*list,partWord); itemComplete(msg.retValue(),*list,partWord);
return true; return true;
} }
else if (partLine == YSTRING("javascript reload") || partLine == YSTRING("javascript allocations")) { else if (partLine == YSTRING("javascript reload") || partLine == YSTRING("javascript allocations total")) {
Lock lck(JsGlobal::s_mutex); Lock lck(JsGlobal::s_mutex);
for (ObjList* o = JsGlobal::globals().skipNull(); o ; o = o->skipNext()) { for (ObjList* o = JsGlobal::globals().skipNull(); o ; o = o->skipNext()) {
JsGlobal* script = static_cast<JsGlobal*>(o->get()); JsGlobal* script = static_cast<JsGlobal*>(o->get());
@ -6545,6 +6774,27 @@ bool JsModule::commandComplete(Message& msg, const String& partLine, const Strin
} }
return true; return true;
} }
else if (partLine == YSTRING("javascript allocations instance")) {
Lock lck(JsGlobal::s_mutex);
for (ObjList* o = JsGlobal::globals().skipNull(); o ; o = o->skipNext()) {
JsGlobal* script = static_cast<JsGlobal*>(o->get());
if (script && script->instances() > 1) {
for (unsigned int i = 0; i <= script->instances(); i++) {
JsGlobalInstance* inst = script->getInstance(i);
if (inst) {
itemComplete(msg.retValue(),inst->toString(),partWord);
TelEngine::destruct(inst);
}
}
}
}
return true;
}
else if (partLine == YSTRING("javascript allocations")) {
itemComplete(msg.retValue(),"total",partWord);
itemComplete(msg.retValue(),"instance",partWord);
return 0;
}
return Module::commandComplete(msg,partLine,partWord); return Module::commandComplete(msg,partLine,partWord);
} }
@ -6629,7 +6879,7 @@ bool JsModule::received(Message& msg, int id)
if (!m_started) { if (!m_started) {
m_started = true; m_started = true;
Configuration cfg(Engine::configFile("javascript")); Configuration cfg(Engine::configFile("javascript"));
JsGlobal::loadScripts(cfg.getSection("late_scripts")); JsGlobal::loadScripts(cfg.getSection("late_scripts"),cfg.getSection("instances"));
} }
return false; return false;
} // switch (id) } // switch (id)
@ -6718,9 +6968,9 @@ void JsModule::initialize()
} }
JsGlobal::markUnused(); JsGlobal::markUnused();
lck.drop(); lck.drop();
JsGlobal::loadScripts(cfg.getSection("scripts")); JsGlobal::loadScripts(cfg.getSection("scripts"),cfg.getSection("instances"));
if (m_started) if (m_started)
JsGlobal::loadScripts(cfg.getSection("late_scripts")); JsGlobal::loadScripts(cfg.getSection("late_scripts"),cfg.getSection("instances"));
JsGlobal::reloadDynamic(); JsGlobal::reloadDynamic();
JsGlobal::freeUnused(); JsGlobal::freeUnused();
} }