Browse Source

feat: Add thread IDs

Matthias Ladkau 3 years ago
parent
commit
490ce0bb0f

+ 2 - 1
cli/tool/debug.go

@@ -146,6 +146,7 @@ func (s *debugTelnetServer) Run() {
 HandleConnection handles an incoming connection.
 */
 func (s *debugTelnetServer) HandleConnection(conn net.Conn) {
+	tid := s.interpreter.RuntimeProvider.NewThreadID()
 	inputReader := bufio.NewReader(conn)
 	outputTerminal := interpreter.OutputTerminal(&bufioWriterShim{bufio.NewWriter(conn)})
 
@@ -163,7 +164,7 @@ func (s *debugTelnetServer) HandleConnection(conn net.Conn) {
 				break
 			}
 
-			s.interpreter.HandleInput(outputTerminal, line)
+			s.interpreter.HandleInput(outputTerminal, line, tid)
 		}
 
 		if err != nil {

+ 8 - 5
cli/tool/interpret.go

@@ -162,6 +162,8 @@ func (i *CLIInterpreter) Interpret(interactive bool) error {
 
 		if err = i.CreateRuntimeProvider("console"); err == nil {
 
+			tid := i.RuntimeProvider.NewThreadID()
+
 			if interactive {
 				if lll, ok := i.RuntimeProvider.Logger.(*util.LogLevelLogger); ok {
 					fmt.Print(fmt.Sprintf("Log level: %v - ", lll.Level()))
@@ -185,7 +187,7 @@ func (i *CLIInterpreter) Interpret(interactive bool) error {
 
 				if ast, err = parser.ParseWithRuntime(initFileName, string(initFile), i.RuntimeProvider); err == nil {
 					if err = ast.Runtime.Validate(); err == nil {
-						_, err = ast.Runtime.Eval(i.GlobalVS, make(map[string]interface{}))
+						_, err = ast.Runtime.Eval(i.GlobalVS, make(map[string]interface{}), tid)
 					}
 				}
 			}
@@ -221,7 +223,7 @@ func (i *CLIInterpreter) Interpret(interactive bool) error {
 								for err == nil && !isExitLine(line) {
 									trimmedLine := strings.TrimSpace(line)
 
-									i.HandleInput(clt, trimmedLine)
+									i.HandleInput(clt, trimmedLine, tid)
 
 									line, err = clt.NextLine()
 								}
@@ -238,9 +240,10 @@ func (i *CLIInterpreter) Interpret(interactive bool) error {
 
 /*
 HandleInput handles input to this interpreter. It parses a given input line
-and outputs on the given output terminal.
+and outputs on the given output terminal. Requires a thread ID of the executing
+thread - use the RuntimeProvider to generate a unique one.
 */
-func (i *CLIInterpreter) HandleInput(ot interpreter.OutputTerminal, line string) {
+func (i *CLIInterpreter) HandleInput(ot interpreter.OutputTerminal, line string, tid uint64) {
 
 	// Process the entered line
 
@@ -278,7 +281,7 @@ func (i *CLIInterpreter) HandleInput(ot interpreter.OutputTerminal, line string)
 
 			if ierr = ast.Runtime.Validate(); ierr == nil {
 
-				if res, ierr = ast.Runtime.Eval(i.GlobalVS, make(map[string]interface{})); ierr == nil && res != nil {
+				if res, ierr = ast.Runtime.Eval(i.GlobalVS, make(map[string]interface{}), tid); ierr == nil && res != nil {
 					ot.WriteString(fmt.Sprintln(res))
 				}
 			}

+ 27 - 9
engine/pool/threadpool.go

@@ -33,9 +33,10 @@ Task is a task which should be run in a thread.
 type Task interface {
 
 	/*
-		Run the task.
+		Run the task. The function gets the unique thread ID of the worker
+		which executes the task.
 	*/
-	Run() error
+	Run(tid uint64) error
 
 	/*
 		HandleError handles an error which occurred during the run method.
@@ -124,7 +125,9 @@ type ThreadPool struct {
 
 	// Worker regulation
 
-	workerIDCount uint64                       // Id counter for worker tasks
+	workerIDCount uint64      // Id counter for worker tasks
+	workerIDLock  *sync.Mutex // Lock for ID generation
+
 	workerMap     map[uint64]*ThreadPoolWorker // Map of all workers
 	workerIdleMap map[uint64]*ThreadPoolWorker // Map of all idle workers
 	workerMapLock *sync.Mutex                  // Lock for worker map
@@ -156,7 +159,7 @@ NewThreadPoolWithQueue creates a new thread pool with a specific task queue.
 */
 func NewThreadPoolWithQueue(q TaskQueue) *ThreadPool {
 	return &ThreadPool{q, &sync.Mutex{},
-		0, make(map[uint64]*ThreadPoolWorker),
+		0, &sync.Mutex{}, make(map[uint64]*ThreadPoolWorker),
 		make(map[uint64]*ThreadPoolWorker), &sync.Mutex{},
 		0, sync.NewCond(&sync.Mutex{}), &sync.Mutex{},
 		math.MaxInt32, func() {}, false, 0, func() {}, false}
@@ -254,6 +257,21 @@ func (tp *ThreadPool) getTask() Task {
 	return nil
 }
 
+/*
+NewThreadID creates a new thread ID unique to this pool.
+*/
+func (tp *ThreadPool) NewThreadID() uint64 {
+
+	tp.workerIDLock.Lock()
+
+	res := tp.workerIDCount
+	tp.workerIDCount++
+
+	tp.workerIDLock.Unlock()
+
+	return res
+}
+
 /*
 SetWorkerCount sets the worker count of this pool. If the wait flag is true then
 this call will return after the pool has reached the requested worker count.
@@ -279,10 +297,10 @@ func (tp *ThreadPool) SetWorkerCount(count int, wait bool) {
 		tp.workerKill = 0
 
 		for len(tp.workerMap) != count {
-			worker := &ThreadPoolWorker{tp.workerIDCount, tp}
+			tid := tp.NewThreadID()
+			worker := &ThreadPoolWorker{tid, tp}
 			go worker.run()
-			tp.workerMap[tp.workerIDCount] = worker
-			tp.workerIDCount++
+			tp.workerMap[tid] = worker
 		}
 
 		tp.workerMapLock.Unlock()
@@ -480,7 +498,7 @@ func (w *ThreadPoolWorker) run() {
 
 		// Run the task
 
-		if err := task.Run(); err != nil {
+		if err := task.Run(w.id); err != nil {
 			task.HandleError(err)
 		}
 
@@ -508,7 +526,7 @@ type idleTask struct {
 /*
 Run the idle task.
 */
-func (t *idleTask) Run() error {
+func (t *idleTask) Run(tid uint64) error {
 	t.tp.newTaskCond.L.Lock()
 	t.tp.newTaskCond.Wait()
 	t.tp.newTaskCond.L.Unlock()

+ 4 - 4
engine/pool/threadpool_test.go

@@ -23,7 +23,7 @@ type testTask struct {
 	errorHandler func(e error)
 }
 
-func (t *testTask) Run() error {
+func (t *testTask) Run(tid uint64) error {
 	return t.task()
 }
 
@@ -77,21 +77,21 @@ func TestDefaultTaskQueue(t *testing.T) {
 
 	// Execute the functions
 
-	tq.Pop().Run()
+	tq.Pop().Run(0)
 
 	if res := tq.Size(); res != 2 {
 		t.Error("Unexpected result: ", res)
 		return
 	}
 
-	tq.Pop().Run()
+	tq.Pop().Run(0)
 
 	if res := tq.Size(); res != 1 {
 		t.Error("Unexpected result: ", res)
 		return
 	}
 
-	tq.Pop().Run()
+	tq.Pop().Run(0)
 
 	if res := tq.Size(); res != 0 {
 		t.Error("Unexpected result: ", res)

+ 18 - 5
engine/processor.go

@@ -30,6 +30,11 @@ type Processor interface {
 	*/
 	ID() uint64
 
+	/*
+	   ThreadPool returns the thread pool which this processor is using.
+	*/
+	ThreadPool() *pool.ThreadPool
+
 	/*
 	   Workers returns the number of threads of this processor.
 	*/
@@ -110,10 +115,11 @@ type Processor interface {
 	IsTriggering(event *Event) bool
 
 	/*
-	   ProcessEvent processes an event by determining which rules trigger and match
-	   the given event.
+		ProcessEvent processes an event by determining which rules trigger and match
+		the given event. This function must receive a unique thread ID from the
+		executing thread.
 	*/
-	ProcessEvent(event *Event, parent Monitor) map[string]error
+	ProcessEvent(tid uint64, event *Event, parent Monitor) map[string]error
 
 	/*
 	   String returns a string representation the processor.
@@ -157,6 +163,13 @@ func (p *eventProcessor) ID() uint64 {
 	return p.id
 }
 
+/*
+ThreadPool returns the thread pool which this processor is using.
+*/
+func (p *eventProcessor) ThreadPool() *pool.ThreadPool {
+	return p.pool
+}
+
 /*
 Workers returns the number of threads of this processor.
 */
@@ -414,7 +427,7 @@ func (p *eventProcessor) IsTriggering(event *Event) bool {
 ProcessEvent processes an event by determining which rules trigger and match
 the given event.
 */
-func (p *eventProcessor) ProcessEvent(event *Event, parent Monitor) map[string]error {
+func (p *eventProcessor) ProcessEvent(tid uint64, event *Event, parent Monitor) map[string]error {
 	var rulesTriggering []*Rule
 	var rulesExecuting []*Rule
 
@@ -459,7 +472,7 @@ func (p *eventProcessor) ProcessEvent(event *Event, parent Monitor) map[string]e
 	EventTracer.record(event, "eventProcessor.ProcessEvent", "Running rules: ", rulesExecuting)
 
 	for _, rule := range rulesExecuting {
-		if err := rule.Action(p, parent, event); err != nil {
+		if err := rule.Action(p, parent, event, tid); err != nil {
 			errors[rule.Name] = err
 		}
 		if p.failOnFirstError && len(errors) > 0 {

+ 17 - 12
engine/processor_test.go

@@ -64,7 +64,7 @@ func TestProcessorSimpleCascade(t *testing.T) {
 		nil,                                    // No state match
 		2,                                      // Priority of the rule
 		[]string{"TestRule3", "TestRule3Copy"}, // List of suppressed rules by this rule
-		func(p Processor, m Monitor, e *Event) error { // Action of the rule
+		func(p Processor, m Monitor, e *Event, tid uint64) error { // Action of the rule
 			log.WriteString("TestRule1\n")
 
 			// Add another event
@@ -90,7 +90,7 @@ func TestProcessorSimpleCascade(t *testing.T) {
 		nil,                     // No state match
 		5,                       // Priority of the rule
 		nil,                     // List of suppressed rules by this rule
-		func(p Processor, m Monitor, e *Event) error { // Action of the rule
+		func(p Processor, m Monitor, e *Event, tid uint64) error { // Action of the rule
 			log.WriteString("TestRule2\n")
 			return nil
 		},
@@ -104,7 +104,7 @@ func TestProcessorSimpleCascade(t *testing.T) {
 		nil,                     // No state match
 		0,                       // Priority of the rule
 		nil,                     // List of suppressed rules by this rule
-		func(p Processor, m Monitor, e *Event) error { // Action of the rule
+		func(p Processor, m Monitor, e *Event, tid uint64) error { // Action of the rule
 			log.WriteString("TestRule3\n")
 			return nil
 		},
@@ -272,7 +272,7 @@ func TestProcessorSimplePriorities(t *testing.T) {
 			nil,                          // No state match
 			0,                            // Priority of the rule
 			nil,                          // List of suppressed rules by this rule
-			func(p Processor, m Monitor, e *Event) error { // Action of the rule
+			func(p Processor, m Monitor, e *Event, tid uint64) error { // Action of the rule
 				logLock.Lock()
 				log.WriteString("TestRule1\n")
 				logLock.Unlock()
@@ -289,7 +289,7 @@ func TestProcessorSimplePriorities(t *testing.T) {
 			nil,                          // No state match
 			0,                            // Priority of the rule
 			nil,                          // List of suppressed rules by this rule
-			func(p Processor, m Monitor, e *Event) error { // Action of the rule
+			func(p Processor, m Monitor, e *Event, tid uint64) error { // Action of the rule
 				logLock.Lock()
 				log.WriteString("TestRule2\n")
 				logLock.Unlock()
@@ -411,7 +411,7 @@ func TestProcessorScopeHandling(t *testing.T) {
 		nil,                     // No state match
 		0,                       // Priority of the rule
 		nil,                     // List of suppressed rules by this rule
-		func(p Processor, m Monitor, e *Event) error { // Action of the rule
+		func(p Processor, m Monitor, e *Event, tid uint64) error { // Action of the rule
 			logLock.Lock()
 			log.WriteString("TestRule1\n")
 			logLock.Unlock()
@@ -428,7 +428,7 @@ func TestProcessorScopeHandling(t *testing.T) {
 		nil,                     // No state match
 		0,                       // Priority of the rule
 		nil,                     // List of suppressed rules by this rule
-		func(p Processor, m Monitor, e *Event) error { // Action of the rule
+		func(p Processor, m Monitor, e *Event, tid uint64) error { // Action of the rule
 			logLock.Lock()
 			log.WriteString("TestRule2\n")
 			logLock.Unlock()
@@ -538,7 +538,7 @@ func TestProcessorStateMatching(t *testing.T) {
 		map[string]interface{}{"name": nil, "test": 1}, // Simple state match
 		0,   // Priority of the rule
 		nil, // List of suppressed rules by this rule
-		func(p Processor, m Monitor, e *Event) error { // Action of the rule
+		func(p Processor, m Monitor, e *Event, tid uint64) error { // Action of the rule
 			logLock.Lock()
 			log.WriteString("TestRule1\n")
 			logLock.Unlock()
@@ -555,7 +555,7 @@ func TestProcessorStateMatching(t *testing.T) {
 		map[string]interface{}{"name": nil, "test": "123"}, // Simple state match
 		0,   // Priority of the rule
 		nil, // List of suppressed rules by this rule
-		func(p Processor, m Monitor, e *Event) error { // Action of the rule
+		func(p Processor, m Monitor, e *Event, tid uint64) error { // Action of the rule
 			logLock.Lock()
 			log.WriteString("TestRule2\n")
 			logLock.Unlock()
@@ -623,6 +623,11 @@ func TestProcessorSimpleErrorHandling(t *testing.T) {
 
 	proc := NewProcessor(10)
 
+	if proc.ThreadPool() == nil {
+		t.Error("Should have a thread pool")
+		return
+	}
+
 	// Add rules to the processor
 
 	rule1 := &Rule{
@@ -633,7 +638,7 @@ func TestProcessorSimpleErrorHandling(t *testing.T) {
 		nil,
 		0,   // Priority of the rule
 		nil, // List of suppressed rules by this rule
-		func(p Processor, m Monitor, e *Event) error { // Action of the rule
+		func(p Processor, m Monitor, e *Event, tid uint64) error { // Action of the rule
 			p.AddEvent(&Event{
 				"event2",
 				[]string{"core", "main", "event2"},
@@ -651,7 +656,7 @@ func TestProcessorSimpleErrorHandling(t *testing.T) {
 		nil,
 		0,   // Priority of the rule
 		nil, // List of suppressed rules by this rule
-		func(p Processor, m Monitor, e *Event) error { // Action of the rule
+		func(p Processor, m Monitor, e *Event, tid uint64) error { // Action of the rule
 			p.AddEvent(&Event{
 				"event3",
 				[]string{"core", "main", "event3"},
@@ -669,7 +674,7 @@ func TestProcessorSimpleErrorHandling(t *testing.T) {
 		nil,
 		0,   // Priority of the rule
 		nil, // List of suppressed rules by this rule
-		func(p Processor, m Monitor, e *Event) error { // Action of the rule
+		func(p Processor, m Monitor, e *Event, tid uint64) error { // Action of the rule
 			return errors.New("testerror2")
 		},
 	}

+ 3 - 2
engine/rule.go

@@ -72,9 +72,10 @@ func (r *Rule) String() string {
 }
 
 /*
-RuleAction is an action which is executed by a matching rule.
+RuleAction is an action which is executed by a matching rule. The action gets
+a unique thread ID from the executing thread.
 */
-type RuleAction func(p Processor, m Monitor, e *Event) error
+type RuleAction func(p Processor, m Monitor, e *Event, tid uint64) error
 
 /*
 RuleIndex is an index for rules. It takes the form of a tree structure in which

+ 8 - 8
engine/rule_test.go

@@ -33,7 +33,7 @@ func TestRuleIndexSimple(t *testing.T) {
 		nil,
 		0,                      // Priority of the rule
 		[]string{"TestRule66"}, // List of suppressed rules by this rule
-		func(p Processor, m Monitor, e *Event) error { // Action of the rule
+		func(p Processor, m Monitor, e *Event, tid uint64) error { // Action of the rule
 			return nil
 		},
 	}
@@ -52,7 +52,7 @@ func TestRuleIndexSimple(t *testing.T) {
 		nil,
 		0,                      // Priority of the rule
 		[]string{"TestRule66"}, // List of suppressed rules by this rule
-		func(p Processor, m Monitor, e *Event) error { // Action of the rule
+		func(p Processor, m Monitor, e *Event, tid uint64) error { // Action of the rule
 			return nil
 		},
 	})
@@ -69,7 +69,7 @@ func TestRuleIndexSimple(t *testing.T) {
 		nil,
 		0,                      // Priority of the rule
 		[]string{"TestRule66"}, // List of suppressed rules by this rule
-		func(p Processor, m Monitor, e *Event) error { // Action of the rule
+		func(p Processor, m Monitor, e *Event, tid uint64) error { // Action of the rule
 			return nil
 		},
 	})
@@ -216,7 +216,7 @@ func TestRuleIndexStateMatch(t *testing.T) {
 		},
 		0,                      // Priority of the rule
 		[]string{"TestRule66"}, // List of suppressed rules by this rule
-		func(p Processor, m Monitor, e *Event) error { // Action of the rule
+		func(p Processor, m Monitor, e *Event, tid uint64) error { // Action of the rule
 			return nil
 		},
 	}
@@ -233,7 +233,7 @@ func TestRuleIndexStateMatch(t *testing.T) {
 		},
 		0,                      // Priority of the rule
 		[]string{"TestRule66"}, // List of suppressed rules by this rule
-		func(p Processor, m Monitor, e *Event) error { // Action of the rule
+		func(p Processor, m Monitor, e *Event, tid uint64) error { // Action of the rule
 			return nil
 		},
 	}
@@ -251,7 +251,7 @@ func TestRuleIndexStateMatch(t *testing.T) {
 		},
 		0,                      // Priority of the rule
 		[]string{"TestRule66"}, // List of suppressed rules by this rule
-		func(p Processor, m Monitor, e *Event) error { // Action of the rule
+		func(p Processor, m Monitor, e *Event, tid uint64) error { // Action of the rule
 			return nil
 		},
 	}
@@ -376,7 +376,7 @@ func TestRuleIndexStateRegexMatch(t *testing.T) {
 		},
 		0,                      // Priority of the rule
 		[]string{"TestRule66"}, // List of suppressed rules by this rule
-		func(p Processor, m Monitor, e *Event) error { // Action of the rule
+		func(p Processor, m Monitor, e *Event, tid uint64) error { // Action of the rule
 			return nil
 		},
 	}
@@ -392,7 +392,7 @@ func TestRuleIndexStateRegexMatch(t *testing.T) {
 		},
 		0,                      // Priority of the rule
 		[]string{"TestRule66"}, // List of suppressed rules by this rule
-		func(p Processor, m Monitor, e *Event) error { // Action of the rule
+		func(p Processor, m Monitor, e *Event, tid uint64) error { // Action of the rule
 			return nil
 		},
 	}

+ 2 - 2
engine/taskqueue.go

@@ -73,10 +73,10 @@ type Task struct {
 /*
 Run the task.
 */
-func (t *Task) Run() error {
+func (t *Task) Run(tid uint64) error {
 	EventTracer.record(t.e, "Task.Run", "Running task")
 
-	errors := t.p.ProcessEvent(t.e, t.m)
+	errors := t.p.ProcessEvent(tid, t.e, t.m)
 
 	if len(errors) > 0 {
 

+ 15 - 15
interpreter/func_provider.go

@@ -112,7 +112,7 @@ type rangeFunc struct {
 /*
 Run executes this function.
 */
-func (rf *rangeFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
+func (rf *rangeFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, tid uint64, args []interface{}) (interface{}, error) {
 	var currVal, to float64
 	var err error
 
@@ -195,7 +195,7 @@ type newFunc struct {
 /*
 Run executes this function.
 */
-func (rf *newFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
+func (rf *newFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, tid uint64, args []interface{}) (interface{}, error) {
 	var res interface{}
 
 	err := fmt.Errorf("Need a map as first parameter")
@@ -214,7 +214,7 @@ func (rf *newFunc) Run(instanceID string, vs parser.Scope, is map[string]interfa
 					initvs := scope.NewScope(fmt.Sprintf("newfunc: %v", instanceID))
 					initis := make(map[string]interface{})
 
-					_, err = initFunc.Run(instanceID, initvs, initis, args[1:])
+					_, err = initFunc.Run(instanceID, initvs, initis, tid, args[1:])
 				}
 			}
 		}
@@ -292,7 +292,7 @@ type lenFunc struct {
 /*
 Run executes this function.
 */
-func (rf *lenFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
+func (rf *lenFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, tid uint64, args []interface{}) (interface{}, error) {
 	var res float64
 
 	err := fmt.Errorf("Need a list or a map as first parameter")
@@ -333,7 +333,7 @@ type delFunc struct {
 /*
 Run executes this function.
 */
-func (rf *delFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
+func (rf *delFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, tid uint64, args []interface{}) (interface{}, error) {
 	var res interface{}
 
 	err := fmt.Errorf("Need a list or a map as first parameter and an index or key as second parameter")
@@ -380,7 +380,7 @@ type addFunc struct {
 /*
 Run executes this function.
 */
-func (rf *addFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
+func (rf *addFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, tid uint64, args []interface{}) (interface{}, error) {
 	var res interface{}
 
 	err := fmt.Errorf("Need a list as first parameter and a value as second parameter")
@@ -427,7 +427,7 @@ type concatFunc struct {
 /*
 Run executes this function.
 */
-func (rf *concatFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
+func (rf *concatFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, tid uint64, args []interface{}) (interface{}, error) {
 	var res interface{}
 
 	err := fmt.Errorf("Need at least two lists as parameters")
@@ -474,7 +474,7 @@ type dumpenvFunc struct {
 /*
 Run executes this function.
 */
-func (rf *dumpenvFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
+func (rf *dumpenvFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, tid uint64, args []interface{}) (interface{}, error) {
 	return vs.String(), nil
 }
 
@@ -498,7 +498,7 @@ type docFunc struct {
 /*
 Run executes this function.
 */
-func (rf *docFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
+func (rf *docFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, tid uint64, args []interface{}) (interface{}, error) {
 	var res interface{}
 	err := fmt.Errorf("Need a function as parameter")
 
@@ -555,7 +555,7 @@ type sleepFunc struct {
 /*
 Run executes this function.
 */
-func (rf *sleepFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
+func (rf *sleepFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, tid uint64, args []interface{}) (interface{}, error) {
 	var res interface{}
 	err := fmt.Errorf("Need number of micro seconds as parameter")
 
@@ -595,7 +595,7 @@ type raise struct {
 /*
 Run executes this function.
 */
-func (rf *raise) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
+func (rf *raise) Run(instanceID string, vs parser.Scope, is map[string]interface{}, tid uint64, args []interface{}) (interface{}, error) {
 	var err error
 	var detailMsg string
 	var detail interface{}
@@ -644,7 +644,7 @@ type addevent struct {
 /*
 Run executes this function.
 */
-func (rf *addevent) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
+func (rf *addevent) Run(instanceID string, vs parser.Scope, is map[string]interface{}, tid uint64, args []interface{}) (interface{}, error) {
 	return rf.addEvent(func(proc engine.Processor, event *engine.Event, scope *engine.RuleScope) (interface{}, error) {
 		var monitor engine.Monitor
 
@@ -735,7 +735,7 @@ type addeventandwait struct {
 /*
 Run executes this function.
 */
-func (rf *addeventandwait) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
+func (rf *addeventandwait) Run(instanceID string, vs parser.Scope, is map[string]interface{}, tid uint64, args []interface{}) (interface{}, error) {
 	return rf.addEvent(func(proc engine.Processor, event *engine.Event, scope *engine.RuleScope) (interface{}, error) {
 		var res []interface{}
 		rm := proc.NewRootMonitor(nil, scope)
@@ -800,7 +800,7 @@ type setCronTrigger struct {
 /*
 Run executes this function.
 */
-func (ct *setCronTrigger) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
+func (ct *setCronTrigger) Run(instanceID string, vs parser.Scope, is map[string]interface{}, tid uint64, args []interface{}) (interface{}, error) {
 	var res interface{}
 	err := fmt.Errorf("Need a cronspec, an event name and an event scope as parameters")
 
@@ -863,7 +863,7 @@ type setPulseTrigger struct {
 /*
 Run executes this function.
 */
-func (pt *setPulseTrigger) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
+func (pt *setPulseTrigger) Run(instanceID string, vs parser.Scope, is map[string]interface{}, tid uint64, args []interface{}) (interface{}, error) {
 	err := fmt.Errorf("Need micro second interval, an event name and an event scope as parameters")
 
 	if len(args) > 2 {

+ 2 - 2
interpreter/func_provider_test.go

@@ -455,12 +455,12 @@ func TestErrorConditions(t *testing.T) {
 
 	rf := &rangeFunc{&inbuildBaseFunc{}}
 
-	if _, err := rf.Run("", nil, nil, nil); err == nil || err.Error() != "Need at least an end range as first parameter" {
+	if _, err := rf.Run("", nil, nil, 0, nil); err == nil || err.Error() != "Need at least an end range as first parameter" {
 		t.Error("Unexpected result:", err)
 		return
 	}
 
-	if _, err := rf.Run("", nil, nil, []interface{}{"bob"}); err == nil || err.Error() != "Parameter 1 should be a number" {
+	if _, err := rf.Run("", nil, nil, 0, []interface{}{"bob"}); err == nil || err.Error() != "Parameter 1 should be a number" {
 		t.Error("Unexpected result:", err)
 		return
 	}

+ 2 - 2
interpreter/main_test.go

@@ -124,7 +124,7 @@ func UnitTestEvalAndASTAndImport(input string, vs parser.Scope, expectedAST stri
 		vs = scope.NewScope(scope.GlobalScope)
 	}
 
-	return ast.Runtime.Eval(vs, make(map[string]interface{}))
+	return ast.Runtime.Eval(vs, make(map[string]interface{}), 0)
 }
 
 /*
@@ -143,7 +143,7 @@ type TestLogger struct {
 	buf *datautil.RingBuffer
 }
 
-func (tl *TestLogger) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
+func (tl *TestLogger) Run(instanceID string, vs parser.Scope, is map[string]interface{}, tid uint64, args []interface{}) (interface{}, error) {
 	tl.buf.Add(fmt.Sprint(args...))
 	return nil, nil
 }

+ 9 - 0
interpreter/provider.go

@@ -193,3 +193,12 @@ NewRuntimeError creates a new RuntimeError object.
 func (erp *ECALRuntimeProvider) NewRuntimeError(t error, d string, node *parser.ASTNode) error {
 	return util.NewRuntimeError(erp.Name, t, d, node)
 }
+
+/*
+NewThreadID creates a new thread ID unique to this runtime provider instance.
+This ID can be safely used for the thread ID when calling Eval on a
+parser.Runtime instance.
+*/
+func (erp *ECALRuntimeProvider) NewThreadID() uint64 {
+	return erp.Processor.ThreadPool().NewThreadID()
+}

+ 20 - 20
interpreter/rt_arithmetic.go

@@ -33,10 +33,10 @@ func plusOpRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Ru
 /*
 Eval evaluate this runtime component.
 */
-func (rt *plusOpRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *plusOpRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var res interface{}
 
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 		// Use as prefix
@@ -44,14 +44,14 @@ func (rt *plusOpRuntime) Eval(vs parser.Scope, is map[string]interface{}) (inter
 		if len(rt.node.Children) == 1 {
 			return rt.numVal(func(n float64) interface{} {
 				return n
-			}, vs, is)
+			}, vs, is, tid)
 		}
 
 		// Use as operation
 
 		res, err = rt.numOp(func(n1 float64, n2 float64) interface{} {
 			return n1 + n2
-		}, vs, is)
+		}, vs, is, tid)
 	}
 
 	return res, err
@@ -71,10 +71,10 @@ func minusOpRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.R
 /*
 Eval evaluate this runtime component.
 */
-func (rt *minusOpRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *minusOpRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var res interface{}
 
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 
@@ -83,14 +83,14 @@ func (rt *minusOpRuntime) Eval(vs parser.Scope, is map[string]interface{}) (inte
 		if len(rt.node.Children) == 1 {
 			return rt.numVal(func(n float64) interface{} {
 				return -n
-			}, vs, is)
+			}, vs, is, tid)
 		}
 
 		// Use as operation
 
 		res, err = rt.numOp(func(n1 float64, n2 float64) interface{} {
 			return n1 - n2
-		}, vs, is)
+		}, vs, is, tid)
 	}
 
 	return res, err
@@ -110,16 +110,16 @@ func timesOpRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.R
 /*
 Eval evaluate this runtime component.
 */
-func (rt *timesOpRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *timesOpRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var res interface{}
 
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 
 		res, err = rt.numOp(func(n1 float64, n2 float64) interface{} {
 			return n1 * n2
-		}, vs, is)
+		}, vs, is, tid)
 	}
 
 	return res, err
@@ -139,16 +139,16 @@ func divOpRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Run
 /*
 Eval evaluate this runtime component.
 */
-func (rt *divOpRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *divOpRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var res interface{}
 
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 
 		res, err = rt.numOp(func(n1 float64, n2 float64) interface{} {
 			return n1 / n2
-		}, vs, is)
+		}, vs, is, tid)
 	}
 
 	return res, err
@@ -168,16 +168,16 @@ func divintOpRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.
 /*
 Eval evaluate this runtime component.
 */
-func (rt *divintOpRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *divintOpRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var res interface{}
 
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 
 		res, err = rt.numOp(func(n1 float64, n2 float64) interface{} {
 			return math.Floor(n1 / n2)
-		}, vs, is)
+		}, vs, is, tid)
 	}
 
 	return res, err
@@ -197,16 +197,16 @@ func modintOpRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.
 /*
 Eval evaluate this runtime component.
 */
-func (rt *modintOpRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *modintOpRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var res interface{}
 
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 
 		res, err = rt.numOp(func(n1 float64, n2 float64) interface{} {
 			return float64(int64(n1) % int64(n2))
-		}, vs, is)
+		}, vs, is, tid)
 	}
 
 	return res, err

+ 5 - 5
interpreter/rt_assign.go

@@ -75,18 +75,18 @@ func (rt *assignmentRuntime) Validate() error {
 /*
 Eval evaluate this runtime component.
 */
-func (rt *assignmentRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
-	_, err := rt.baseRuntime.Eval(vs, is)
+func (rt *assignmentRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 		var val interface{}
 
-		val, err = rt.node.Children[1].Runtime.Eval(vs, is)
+		val, err = rt.node.Children[1].Runtime.Eval(vs, is, tid)
 
 		if err == nil {
 			if len(rt.leftSide) == 1 {
 
-				err = rt.leftSide[0].Set(vs, is, val)
+				err = rt.leftSide[0].Set(vs, is, tid, val)
 
 			} else if valList, ok := val.([]interface{}); ok {
 
@@ -101,7 +101,7 @@ func (rt *assignmentRuntime) Eval(vs parser.Scope, is map[string]interface{}) (i
 
 					for i, v := range rt.leftSide {
 
-						if err = v.Set(vs, is, valList[i]); err != nil {
+						if err = v.Set(vs, is, tid, valList[i]); err != nil {
 							err = rt.erp.NewRuntimeError(util.ErrVarAccess,
 								err.Error(), rt.node)
 							break

+ 47 - 47
interpreter/rt_boolean.go

@@ -36,21 +36,21 @@ func greaterequalOpRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) p
 /*
 Eval evaluate this runtime component.
 */
-func (rt *greaterequalOpRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *greaterequalOpRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var res interface{}
 
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 
 		res, err = rt.numOp(func(n1 float64, n2 float64) interface{} {
 			return n1 >= n2
-		}, vs, is)
+		}, vs, is, tid)
 
 		if err != nil {
 			res, err = rt.strOp(func(n1 string, n2 string) interface{} {
 				return n1 >= n2
-			}, vs, is)
+			}, vs, is, tid)
 		}
 	}
 
@@ -71,21 +71,21 @@ func greaterOpRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser
 /*
 Eval evaluate this runtime component.
 */
-func (rt *greaterOpRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *greaterOpRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var res interface{}
 
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 
 		res, err = rt.numOp(func(n1 float64, n2 float64) interface{} {
 			return n1 > n2
-		}, vs, is)
+		}, vs, is, tid)
 
 		if err != nil {
 			res, err = rt.strOp(func(n1 string, n2 string) interface{} {
 				return n1 > n2
-			}, vs, is)
+			}, vs, is, tid)
 		}
 	}
 
@@ -106,21 +106,21 @@ func lessequalOpRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) pars
 /*
 Eval evaluate this runtime component.
 */
-func (rt *lessequalOpRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *lessequalOpRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var res interface{}
 
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 
 		res, err = rt.numOp(func(n1 float64, n2 float64) interface{} {
 			return n1 <= n2
-		}, vs, is)
+		}, vs, is, tid)
 
 		if err != nil {
 			res, err = rt.strOp(func(n1 string, n2 string) interface{} {
 				return n1 <= n2
-			}, vs, is)
+			}, vs, is, tid)
 		}
 	}
 
@@ -141,21 +141,21 @@ func lessOpRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Ru
 /*
 Eval evaluate this runtime component.
 */
-func (rt *lessOpRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *lessOpRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var res interface{}
 
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 
 		res, err = rt.numOp(func(n1 float64, n2 float64) interface{} {
 			return n1 < n2
-		}, vs, is)
+		}, vs, is, tid)
 
 		if err != nil {
 			res, err = rt.strOp(func(n1 string, n2 string) interface{} {
 				return n1 < n2
-			}, vs, is)
+			}, vs, is, tid)
 		}
 	}
 
@@ -176,16 +176,16 @@ func equalOpRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.R
 /*
 Eval evaluate this runtime component.
 */
-func (rt *equalOpRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *equalOpRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var res interface{}
 
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 
 		res, err = rt.genOp(func(n1 interface{}, n2 interface{}) interface{} {
 			return n1 == n2
-		}, vs, is)
+		}, vs, is, tid)
 	}
 
 	return res, err
@@ -205,16 +205,16 @@ func notequalOpRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parse
 /*
 Eval evaluate this runtime component.
 */
-func (rt *notequalOpRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *notequalOpRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var res interface{}
 
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 
 		res, err = rt.genOp(func(n1 interface{}, n2 interface{}) interface{} {
 			return n1 != n2
-		}, vs, is)
+		}, vs, is, tid)
 	}
 
 	return res, err
@@ -234,16 +234,16 @@ func andOpRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Run
 /*
 Eval evaluate this runtime component.
 */
-func (rt *andOpRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *andOpRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var res interface{}
 
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 
 		res, err = rt.boolOp(func(b1 bool, b2 bool) interface{} {
 			return b1 && b2
-		}, vs, is)
+		}, vs, is, tid)
 	}
 
 	return res, err
@@ -263,16 +263,16 @@ func orOpRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Runt
 /*
 Eval evaluate this runtime component.
 */
-func (rt *orOpRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *orOpRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var res interface{}
 
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 
 		res, err = rt.boolOp(func(b1 bool, b2 bool) interface{} {
 			return b1 || b2
-		}, vs, is)
+		}, vs, is, tid)
 	}
 
 	return res, err
@@ -292,16 +292,16 @@ func notOpRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Run
 /*
 Eval evaluate this runtime component.
 */
-func (rt *notOpRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *notOpRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var res interface{}
 
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 
 		res, err = rt.boolVal(func(b bool) interface{} {
 			return !b
-		}, vs, is)
+		}, vs, is, tid)
 	}
 
 	return res, err
@@ -329,20 +329,20 @@ func likeOpRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Ru
 /*
 Eval evaluate this runtime component.
 */
-func (rt *likeOpRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *likeOpRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var res interface{}
 
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 		errorutil.AssertTrue(len(rt.node.Children) == 2,
 			fmt.Sprint("Operation requires 2 operands", rt.node))
 
-		str, err := rt.node.Children[0].Runtime.Eval(vs, is)
+		str, err := rt.node.Children[0].Runtime.Eval(vs, is, tid)
 		if err == nil {
 			var pattern interface{}
 
-			pattern, err = rt.node.Children[1].Runtime.Eval(vs, is)
+			pattern, err = rt.node.Children[1].Runtime.Eval(vs, is, tid)
 			if err == nil {
 				var re *regexp.Regexp
 
@@ -372,15 +372,15 @@ func beginswithOpRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) par
 /*
 Eval evaluate this runtime component.
 */
-func (rt *beginswithOpRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *beginswithOpRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var res interface{}
 
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 		res, err = rt.strOp(func(s1 string, s2 string) interface{} {
 			return strings.HasPrefix(s1, s2)
-		}, vs, is)
+		}, vs, is, tid)
 	}
 
 	return res, err
@@ -400,15 +400,15 @@ func endswithOpRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parse
 /*
 Eval evaluate this runtime component.
 */
-func (rt *endswithOpRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *endswithOpRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var res interface{}
 
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 		res, err = rt.strOp(func(s1 string, s2 string) interface{} {
 			return strings.HasSuffix(s1, s2)
-		}, vs, is)
+		}, vs, is, tid)
 	}
 
 	return res, err
@@ -428,10 +428,10 @@ func inOpRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Runt
 /*
 Eval evaluate this runtime component.
 */
-func (rt *inOpRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *inOpRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var res interface{}
 
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 		res, err = rt.listOp(func(val interface{}, list []interface{}) interface{} {
@@ -441,7 +441,7 @@ func (rt *inOpRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interfa
 				}
 			}
 			return false
-		}, vs, is)
+		}, vs, is, tid)
 	}
 
 	return res, err
@@ -461,13 +461,13 @@ func notinOpRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.R
 /*
 Eval evaluate this runtime component.
 */
-func (rt *notinOpRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *notinOpRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var res interface{}
 
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
-		if res, err = rt.inOpRuntime.Eval(vs, is); err == nil {
+		if res, err = rt.inOpRuntime.Eval(vs, is, tid); err == nil {
 			res = !res.(bool)
 		}
 	}

+ 6 - 6
interpreter/rt_const.go

@@ -29,8 +29,8 @@ func trueRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Runt
 /*
 Eval evaluate this runtime component.
 */
-func (rt *trueRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
-	_, err := rt.baseRuntime.Eval(vs, is)
+func (rt *trueRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 	return true, err
 }
 
@@ -51,8 +51,8 @@ func falseRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Run
 /*
 Eval evaluate this runtime component.
 */
-func (rt *falseRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
-	_, err := rt.baseRuntime.Eval(vs, is)
+func (rt *falseRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 	return false, err
 }
 
@@ -73,7 +73,7 @@ func nullRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Runt
 /*
 Eval evaluate this runtime component.
 */
-func (rt *nullRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
-	_, err := rt.baseRuntime.Eval(vs, is)
+func (rt *nullRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 	return nil, err
 }

+ 8 - 8
interpreter/rt_func.go

@@ -44,14 +44,14 @@ func (rt *returnRuntime) Validate() error {
 /*
 Eval evaluate this runtime component.
 */
-func (rt *returnRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *returnRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 		var res interface{}
 
-		if res, err = rt.node.Children[0].Runtime.Eval(vs, is); err == nil {
+		if res, err = rt.node.Children[0].Runtime.Eval(vs, is, tid); err == nil {
 			rerr := rt.erp.NewRuntimeError(util.ErrReturn, fmt.Sprintf("Return value: %v", res), rt.node)
 			err = &returnValue{
 				rerr.(*util.RuntimeError),
@@ -86,10 +86,10 @@ func funcRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Runt
 /*
 Eval evaluate this runtime component.
 */
-func (rt *funcRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *funcRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var fc interface{}
 
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 		name := ""
@@ -123,7 +123,7 @@ type function struct {
 Run executes this function. The function is called with parameters and might also
 have a reference to a context state - this.
 */
-func (f *function) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
+func (f *function) Run(instanceID string, vs parser.Scope, is map[string]interface{}, tid uint64, args []interface{}) (interface{}, error) {
 	var res interface{}
 	var err error
 
@@ -164,7 +164,7 @@ func (f *function) Run(instanceID string, vs parser.Scope, is map[string]interfa
 				if i < len(args) {
 					val = args[i]
 				} else {
-					val, err = p.Children[1].Runtime.Eval(vs, is)
+					val, err = p.Children[1].Runtime.Eval(vs, is, tid)
 				}
 			}
 
@@ -178,7 +178,7 @@ func (f *function) Run(instanceID string, vs parser.Scope, is map[string]interfa
 
 		scope.SetParentOfScope(fvs, f.declarationVS)
 
-		res, err = body.Runtime.Eval(fvs, make(map[string]interface{}))
+		res, err = body.Runtime.Eval(fvs, make(map[string]interface{}), tid)
 
 		// Check for return value (delivered as error object)
 

+ 29 - 29
interpreter/rt_general.go

@@ -52,7 +52,7 @@ func (rt *baseRuntime) Validate() error {
 /*
 Eval evaluate this runtime component.
 */
-func (rt *baseRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *baseRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	return nil, nil
 }
 
@@ -92,8 +92,8 @@ func (rt *voidRuntime) Validate() error {
 /*
 Eval evaluate this runtime component.
 */
-func (rt *voidRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
-	return rt.baseRuntime.Eval(vs, is)
+func (rt *voidRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
+	return rt.baseRuntime.Eval(vs, is, tid)
 }
 
 // Import Runtime
@@ -123,8 +123,8 @@ func (rt *importRuntime) Validate() error {
 /*
 Eval evaluate this runtime component.
 */
-func (rt *importRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
-	_, err := rt.baseRuntime.Eval(vs, is)
+func (rt *importRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if rt.erp.ImportLocator == nil {
 		err = rt.erp.NewRuntimeError(util.ErrRuntimeError, "No import locator was specified", rt.node)
@@ -133,7 +133,7 @@ func (rt *importRuntime) Eval(vs parser.Scope, is map[string]interface{}) (inter
 	if err == nil {
 
 		var importPath interface{}
-		if importPath, err = rt.node.Children[0].Runtime.Eval(vs, is); err == nil {
+		if importPath, err = rt.node.Children[0].Runtime.Eval(vs, is, tid); err == nil {
 
 			var codeText string
 			if codeText, err = rt.erp.ImportLocator.Resolve(fmt.Sprint(importPath)); err == nil {
@@ -143,9 +143,9 @@ func (rt *importRuntime) Eval(vs parser.Scope, is map[string]interface{}) (inter
 					if err = ast.Runtime.Validate(); err == nil {
 
 						ivs := scope.NewScope(scope.GlobalScope)
-						if _, err = ast.Runtime.Eval(ivs, make(map[string]interface{})); err == nil {
+						if _, err = ast.Runtime.Eval(ivs, make(map[string]interface{}), tid); err == nil {
 							irt := rt.node.Children[1].Runtime.(*identifierRuntime)
-							irt.Set(vs, is, scope.ToObject(ivs))
+							irt.Set(vs, is, tid, scope.ToObject(ivs))
 						}
 					}
 				}
@@ -188,8 +188,8 @@ func (rt *invalidRuntime) Validate() error {
 /*
 Eval evaluate this runtime component.
 */
-func (rt *invalidRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
-	_, err := rt.baseRuntime.Eval(vs, is)
+func (rt *invalidRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 	if err == nil {
 		err = rt.erp.NewRuntimeError(util.ErrInvalidConstruct, fmt.Sprintf("Unknown node: %s", rt.node.Name), rt.node)
 	}
@@ -225,14 +225,14 @@ func (rt *operatorRuntime) errorDetailString(token *parser.LexToken, opVal inter
 numVal returns a transformed number value.
 */
 func (rt *operatorRuntime) numVal(op func(float64) interface{}, vs parser.Scope,
-	is map[string]interface{}) (interface{}, error) {
+	is map[string]interface{}, tid uint64) (interface{}, error) {
 
 	var ret interface{}
 
 	errorutil.AssertTrue(len(rt.node.Children) == 1,
 		fmt.Sprint("Operation requires 1 operand", rt.node))
 
-	res, err := rt.node.Children[0].Runtime.Eval(vs, is)
+	res, err := rt.node.Children[0].Runtime.Eval(vs, is, tid)
 	if err == nil {
 
 		// Check if the value is a number
@@ -257,14 +257,14 @@ func (rt *operatorRuntime) numVal(op func(float64) interface{}, vs parser.Scope,
 boolVal returns a transformed boolean value.
 */
 func (rt *operatorRuntime) boolVal(op func(bool) interface{},
-	vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+	vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 
 	var ret interface{}
 
 	errorutil.AssertTrue(len(rt.node.Children) == 1,
 		fmt.Sprint("Operation requires 1 operand", rt.node))
 
-	res, err := rt.node.Children[0].Runtime.Eval(vs, is)
+	res, err := rt.node.Children[0].Runtime.Eval(vs, is, tid)
 	if err == nil {
 
 		resBool, ok := res.(bool)
@@ -284,7 +284,7 @@ func (rt *operatorRuntime) boolVal(op func(bool) interface{},
 numOp executes an operation on two number values.
 */
 func (rt *operatorRuntime) numOp(op func(float64, float64) interface{},
-	vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+	vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var ok bool
 	var res1, res2 interface{}
 	var err error
@@ -292,8 +292,8 @@ func (rt *operatorRuntime) numOp(op func(float64, float64) interface{},
 	errorutil.AssertTrue(len(rt.node.Children) == 2,
 		fmt.Sprint("Operation requires 2 operands", rt.node))
 
-	if res1, err = rt.node.Children[0].Runtime.Eval(vs, is); err == nil {
-		if res2, err = rt.node.Children[1].Runtime.Eval(vs, is); err == nil {
+	if res1, err = rt.node.Children[0].Runtime.Eval(vs, is, tid); err == nil {
+		if res2, err = rt.node.Children[1].Runtime.Eval(vs, is, tid); err == nil {
 			var res1Num, res2Num float64
 
 			if res1Num, ok = res1.(float64); !ok {
@@ -320,18 +320,18 @@ func (rt *operatorRuntime) numOp(op func(float64, float64) interface{},
 genOp executes an operation on two general values.
 */
 func (rt *operatorRuntime) genOp(op func(interface{}, interface{}) interface{},
-	vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+	vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 
 	var ret interface{}
 
 	errorutil.AssertTrue(len(rt.node.Children) == 2,
 		fmt.Sprint("Operation requires 2 operands", rt.node))
 
-	res1, err := rt.node.Children[0].Runtime.Eval(vs, is)
+	res1, err := rt.node.Children[0].Runtime.Eval(vs, is, tid)
 	if err == nil {
 		var res2 interface{}
 
-		if res2, err = rt.node.Children[1].Runtime.Eval(vs, is); err == nil {
+		if res2, err = rt.node.Children[1].Runtime.Eval(vs, is, tid); err == nil {
 			ret = op(res1, res2)
 		}
 	}
@@ -343,18 +343,18 @@ func (rt *operatorRuntime) genOp(op func(interface{}, interface{}) interface{},
 strOp executes an operation on two string values.
 */
 func (rt *operatorRuntime) strOp(op func(string, string) interface{},
-	vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+	vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 
 	var ret interface{}
 
 	errorutil.AssertTrue(len(rt.node.Children) == 2,
 		fmt.Sprint("Operation requires 2 operands", rt.node))
 
-	res1, err := rt.node.Children[0].Runtime.Eval(vs, is)
+	res1, err := rt.node.Children[0].Runtime.Eval(vs, is, tid)
 	if err == nil {
 		var res2 interface{}
 
-		if res2, err = rt.node.Children[1].Runtime.Eval(vs, is); err == nil {
+		if res2, err = rt.node.Children[1].Runtime.Eval(vs, is, tid); err == nil {
 			ret = op(fmt.Sprint(res1), fmt.Sprint(res2))
 		}
 	}
@@ -366,18 +366,18 @@ func (rt *operatorRuntime) strOp(op func(string, string) interface{},
 boolOp executes an operation on two boolean values.
 */
 func (rt *operatorRuntime) boolOp(op func(bool, bool) interface{},
-	vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+	vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 
 	var res interface{}
 
 	errorutil.AssertTrue(len(rt.node.Children) == 2,
 		fmt.Sprint("Operation requires 2 operands", rt.node))
 
-	res1, err := rt.node.Children[0].Runtime.Eval(vs, is)
+	res1, err := rt.node.Children[0].Runtime.Eval(vs, is, tid)
 	if err == nil {
 		var res2 interface{}
 
-		if res2, err = rt.node.Children[1].Runtime.Eval(vs, is); err == nil {
+		if res2, err = rt.node.Children[1].Runtime.Eval(vs, is, tid); err == nil {
 
 			res1bool, ok := res1.(bool)
 			if !ok {
@@ -402,18 +402,18 @@ func (rt *operatorRuntime) boolOp(op func(bool, bool) interface{},
 listOp executes an operation on a value and a list.
 */
 func (rt *operatorRuntime) listOp(op func(interface{}, []interface{}) interface{},
-	vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+	vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 
 	var res interface{}
 
 	errorutil.AssertTrue(len(rt.node.Children) == 2,
 		fmt.Sprint("Operation requires 2 operands", rt.node))
 
-	res1, err := rt.node.Children[0].Runtime.Eval(vs, is)
+	res1, err := rt.node.Children[0].Runtime.Eval(vs, is, tid)
 	if err == nil {
 		var res2 interface{}
 
-		if res2, err = rt.node.Children[1].Runtime.Eval(vs, is); err == nil {
+		if res2, err = rt.node.Children[1].Runtime.Eval(vs, is, tid); err == nil {
 
 			res2list, ok := res2.([]interface{})
 			if !ok {

+ 14 - 5
interpreter/rt_general_test.go

@@ -18,17 +18,26 @@ import (
 	"devt.de/krotik/ecal/util"
 )
 
-func TestGeneralErrorCases(t *testing.T) {
+func TestGeneralCases(t *testing.T) {
+
+	rt := NewECALRuntimeProvider("a", nil, nil)
+	id1 := rt.NewThreadID()
+	id2 := rt.NewThreadID()
+
+	if id1 == id2 {
+		t.Error("Thread ids should not be the same:", id1)
+		return
+	}
 
 	n, _ := parser.Parse("a", "a")
-	inv := &invalidRuntime{newBaseRuntime(NewECALRuntimeProvider("a", nil, nil), n)}
+	inv := &invalidRuntime{newBaseRuntime(rt, n)}
 
 	if err := inv.Validate().Error(); err != "ECAL error in a: Invalid construct (Unknown node: identifier) (Line:1 Pos:1)" {
 		t.Error("Unexpected result:", err)
 		return
 	}
 
-	if _, err := inv.Eval(nil, nil); err.Error() != "ECAL error in a: Invalid construct (Unknown node: identifier) (Line:1 Pos:1)" {
+	if _, err := inv.Eval(nil, nil, 0); err.Error() != "ECAL error in a: Invalid construct (Unknown node: identifier) (Line:1 Pos:1)" {
 		t.Error("Unexpected result:", err)
 		return
 	}
@@ -51,7 +60,7 @@ func TestGeneralErrorCases(t *testing.T) {
 	void := &voidRuntime{newBaseRuntime(NewECALRuntimeProvider("a", nil, nil), n)}
 	n.Runtime = void
 
-	if res, err := void.Eval(nil, nil); err != nil || res != nil {
+	if res, err := void.Eval(nil, nil, 0); err != nil || res != nil {
 		t.Error("Unexpected result:", res, err)
 		return
 	}
@@ -95,7 +104,7 @@ statements
 	imp.erp = NewECALRuntimeProvider("ECALTestRuntime", nil, nil)
 	imp.erp.ImportLocator = nil
 
-	if res, err := imp.Eval(nil, nil); err == nil || err.Error() != "ECAL error in ECALTestRuntime: Runtime error (No import locator was specified) (Line:1 Pos:1)" {
+	if res, err := imp.Eval(nil, nil, 0); err == nil || err.Error() != "ECAL error in ECALTestRuntime: Runtime error (No import locator was specified) (Line:1 Pos:1)" {
 		t.Error("Unexpected result:", res, err)
 		return
 	}

+ 16 - 16
interpreter/rt_identifier.go

@@ -38,11 +38,11 @@ func identifierRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parse
 /*
 Eval evaluate this runtime component.
 */
-func (rt *identifierRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *identifierRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var res interface{}
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 	if err == nil {
-		res, err = rt.resolveValue(vs, is, rt.node)
+		res, err = rt.resolveValue(vs, is, tid, rt.node)
 	}
 	return res, err
 }
@@ -50,7 +50,7 @@ func (rt *identifierRuntime) Eval(vs parser.Scope, is map[string]interface{}) (i
 /*
 resolveValue resolves the value of this identifier.
 */
-func (rt *identifierRuntime) resolveValue(vs parser.Scope, is map[string]interface{}, node *parser.ASTNode) (interface{}, error) {
+func (rt *identifierRuntime) resolveValue(vs parser.Scope, is map[string]interface{}, tid uint64, node *parser.ASTNode) (interface{}, error) {
 	var anode *parser.ASTNode
 	var astring string
 	var result interface{}
@@ -80,7 +80,7 @@ func (rt *identifierRuntime) resolveValue(vs parser.Scope, is map[string]interfa
 		return res
 	}
 
-	anode, astring, err = buildAccessString(rt.erp, vs, is, node, node.Token.Val)
+	anode, astring, err = buildAccessString(rt.erp, vs, is, tid, node, node.Token.Val)
 
 	if len(node.Children) == 0 {
 
@@ -101,7 +101,7 @@ func (rt *identifierRuntime) resolveValue(vs parser.Scope, is map[string]interfa
 
 				if funcCallInAccessStringExecuted {
 
-					result, err = rt.resolveFunction(astring, vs, is, rerr.Node, result, err)
+					result, err = rt.resolveFunction(astring, vs, is, tid, rerr.Node, result, err)
 
 					node = functionResolved(astring, anode)
 
@@ -112,11 +112,11 @@ func (rt *identifierRuntime) resolveValue(vs parser.Scope, is map[string]interfa
 						vs = scope.NewScope("funcresult")
 						vs.SetValue(node.Token.Val, result)
 
-						result, err = rt.resolveValue(vs, is, node)
+						result, err = rt.resolveValue(vs, is, tid, node)
 					}
 				} else {
 
-					result, err = rt.resolveFunction(astring, vs, is, node, result, err)
+					result, err = rt.resolveFunction(astring, vs, is, tid, node, result, err)
 				}
 			}
 		}
@@ -129,7 +129,7 @@ func (rt *identifierRuntime) resolveValue(vs parser.Scope, is map[string]interfa
 resolveFunction execute function calls and return the result.
 */
 func (rt *identifierRuntime) resolveFunction(astring string, vs parser.Scope, is map[string]interface{},
-	node *parser.ASTNode, result interface{}, err error) (interface{}, error) {
+	tid uint64, node *parser.ASTNode, result interface{}, err error) (interface{}, error) {
 
 	is["erp"] = rt.erp      // All functions have access to the ECAL Runtime Provider
 	is["astnode"] = rt.node // ... and the AST node
@@ -169,7 +169,7 @@ func (rt *identifierRuntime) resolveFunction(astring string, vs parser.Scope, is
 					var val interface{}
 
 					if err == nil {
-						val, err = c.Runtime.Eval(vs, make(map[string]interface{}))
+						val, err = c.Runtime.Eval(vs, make(map[string]interface{}), tid)
 						args = append(args, val)
 					}
 				}
@@ -198,7 +198,7 @@ func (rt *identifierRuntime) resolveFunction(astring string, vs parser.Scope, is
 
 						// Execute the function and
 
-						result, err = funcObj.Run(rt.instanceID, vs, is, args)
+						result, err = funcObj.Run(rt.instanceID, vs, is, tid, args)
 
 						_, ok1 := err.(*util.RuntimeErrorWithDetail)
 						_, ok2 := err.(*util.RuntimeErrorWithDetail)
@@ -235,7 +235,7 @@ func (rt *identifierRuntime) resolveFunction(astring string, vs parser.Scope, is
 /*
 Set sets a value to this identifier.
 */
-func (rt *identifierRuntime) Set(vs parser.Scope, is map[string]interface{}, value interface{}) error {
+func (rt *identifierRuntime) Set(vs parser.Scope, is map[string]interface{}, tid uint64, value interface{}) error {
 	var err error
 
 	if len(rt.node.Children) == 0 {
@@ -247,7 +247,7 @@ func (rt *identifierRuntime) Set(vs parser.Scope, is map[string]interface{}, val
 	} else {
 		var as string
 
-		_, as, err = buildAccessString(rt.erp, vs, is, rt.node, rt.node.Token.Val)
+		_, as, err = buildAccessString(rt.erp, vs, is, tid, rt.node, rt.node.Token.Val)
 
 		if err == nil {
 
@@ -264,7 +264,7 @@ func (rt *identifierRuntime) Set(vs parser.Scope, is map[string]interface{}, val
 buildAccessString builds an access string using a given node and a prefix.
 */
 func buildAccessString(erp *ECALRuntimeProvider, vs parser.Scope, is map[string]interface{},
-	node *parser.ASTNode, prefix string) (*parser.ASTNode, string, error) {
+	tid uint64, node *parser.ASTNode, prefix string) (*parser.ASTNode, string, error) {
 
 	var err error
 	res := prefix
@@ -281,7 +281,7 @@ func buildAccessString(erp *ECALRuntimeProvider, vs parser.Scope, is map[string]
 
 			if c.Name == parser.NodeCOMPACCESS {
 				var val interface{}
-				val, err = c.Children[0].Runtime.Eval(vs, is)
+				val, err = c.Children[0].Runtime.Eval(vs, is, tid)
 				res = fmt.Sprintf("%v.%v", res, val)
 
 				if len(node.Children) > i+1 && node.Children[i+1].Name == parser.NodeFUNCCALL {
@@ -303,7 +303,7 @@ func buildAccessString(erp *ECALRuntimeProvider, vs parser.Scope, is map[string]
 					break
 				}
 
-				node, res, err = buildAccessString(erp, vs, is, c, res)
+				node, res, err = buildAccessString(erp, vs, is, tid, c, res)
 			}
 		}
 	}

+ 10 - 10
interpreter/rt_sink.go

@@ -72,13 +72,13 @@ func (rt *sinkRuntime) Validate() error {
 /*
 Eval evaluate this runtime component.
 */
-func (rt *sinkRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *sinkRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var kindMatch, scopeMatch, suppresses []string
 	var stateMatch map[string]interface{}
 	var priority int
 	var statements *parser.ASTNode
 
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 		// Create default scope
@@ -94,7 +94,7 @@ func (rt *sinkRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interfa
 		makeStringList := func(child *parser.ASTNode) ([]string, error) {
 			var ret []string
 
-			val, err := child.Runtime.Eval(vs, is)
+			val, err := child.Runtime.Eval(vs, is, tid)
 
 			if err == nil {
 				for _, v := range val.([]interface{}) {
@@ -123,7 +123,7 @@ func (rt *sinkRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interfa
 				var val interface{}
 				stateMatch = make(map[string]interface{})
 
-				if val, err = child.Runtime.Eval(vs, is); err == nil {
+				if val, err = child.Runtime.Eval(vs, is, tid); err == nil {
 					for k, v := range val.(map[interface{}]interface{}) {
 						stateMatch[fmt.Sprint(k)] = v
 					}
@@ -133,7 +133,7 @@ func (rt *sinkRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interfa
 			case parser.NodePRIORITY:
 				var val interface{}
 
-				if val, err = child.Runtime.Eval(vs, is); err == nil {
+				if val, err = child.Runtime.Eval(vs, is, tid); err == nil {
 					priority = int(math.Floor(val.(float64)))
 				}
 				break
@@ -171,7 +171,7 @@ func (rt *sinkRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interfa
 				StateMatch:      stateMatch, // No state match
 				Priority:        priority,   // Priority of the rule
 				SuppressionList: suppresses, // List of suppressed rules by this rule
-				Action: func(p engine.Processor, m engine.Monitor, e *engine.Event) error { // Action of the rule
+				Action: func(p engine.Processor, m engine.Monitor, e *engine.Event, tid uint64) error { // Action of the rule
 
 					// Create a new root variable scope
 
@@ -193,7 +193,7 @@ func (rt *sinkRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interfa
 					if err == nil {
 						scope.SetParentOfScope(sinkVS, vs)
 
-						if _, err = statements.Runtime.Eval(sinkVS, sinkIs); err != nil {
+						if _, err = statements.Runtime.Eval(sinkVS, sinkIs, tid); err != nil {
 
 							if sre, ok := err.(*util.RuntimeErrorWithDetail); ok {
 								sre.Environment = sinkVS
@@ -238,14 +238,14 @@ type sinkDetailRuntime struct {
 /*
 Eval evaluate this runtime component.
 */
-func (rt *sinkDetailRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *sinkDetailRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var ret interface{}
 
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 
-		if ret, err = rt.node.Children[0].Runtime.Eval(vs, is); err == nil {
+		if ret, err = rt.node.Children[0].Runtime.Eval(vs, is, tid); err == nil {
 
 			// Check value is of expected type
 

+ 30 - 30
interpreter/rt_statements.go

@@ -40,13 +40,13 @@ func statementsRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parse
 /*
 Eval evaluate this runtime component.
 */
-func (rt *statementsRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *statementsRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var res interface{}
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 		for _, child := range rt.node.Children {
-			if res, err = child.Runtime.Eval(vs, is); err != nil {
+			if res, err = child.Runtime.Eval(vs, is, tid); err != nil {
 				return nil, err
 			}
 		}
@@ -75,8 +75,8 @@ func ifRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Runtim
 /*
 Eval evaluate this runtime component.
 */
-func (rt *ifRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
-	_, err := rt.baseRuntime.Eval(vs, is)
+func (rt *ifRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 
@@ -90,13 +90,13 @@ func (rt *ifRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface
 			// Evaluate guard
 
 			if err == nil {
-				guardres, err = rt.node.Children[offset].Runtime.Eval(vs, is)
+				guardres, err = rt.node.Children[offset].Runtime.Eval(vs, is, tid)
 
 				if err == nil && guardres.(bool) {
 
 					// The guard holds true so we execture its statements
 
-					return rt.node.Children[offset+1].Runtime.Eval(vs, is)
+					return rt.node.Children[offset+1].Runtime.Eval(vs, is, tid)
 				}
 			}
 		}
@@ -125,17 +125,17 @@ func guardRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Run
 /*
 Eval evaluate this runtime component.
 */
-func (rt *guardRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *guardRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var res interface{}
 
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 		var ret interface{}
 
 		// Evaluate the condition
 
-		ret, err = rt.node.Children[0].Runtime.Eval(vs, is)
+		ret, err = rt.node.Children[0].Runtime.Eval(vs, is, tid)
 
 		// Guard returns always a boolean
 
@@ -206,9 +206,9 @@ func (rt *loopRuntime) Validate() error {
 /*
 Eval evaluate this runtime component.
 */
-func (rt *loopRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *loopRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 		var guardres interface{}
@@ -225,13 +225,13 @@ func (rt *loopRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interfa
 
 			// Evaluate guard
 
-			guardres, err = rt.node.Children[0].Runtime.Eval(vs, is)
+			guardres, err = rt.node.Children[0].Runtime.Eval(vs, is, tid)
 
 			for err == nil && guardres.(bool) {
 
 				// Execute block
 
-				_, err = rt.node.Children[1].Runtime.Eval(vs, is)
+				_, err = rt.node.Children[1].Runtime.Eval(vs, is, tid)
 
 				// Check for continue
 
@@ -247,7 +247,7 @@ func (rt *loopRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interfa
 
 					// Evaluate guard
 
-					guardres, err = rt.node.Children[0].Runtime.Eval(vs, is)
+					guardres, err = rt.node.Children[0].Runtime.Eval(vs, is, tid)
 				}
 			}
 
@@ -257,7 +257,7 @@ func (rt *loopRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interfa
 
 			it := rt.node.Children[0].Children[1]
 
-			val, err = it.Runtime.Eval(vs, is)
+			val, err = it.Runtime.Eval(vs, is, tid)
 
 			// Create an iterator object
 
@@ -266,7 +266,7 @@ func (rt *loopRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interfa
 				// We got an iterator - all subsequent calls will return values
 
 				iterator = func() (interface{}, error) {
-					return it.Runtime.Eval(vs, is)
+					return it.Runtime.Eval(vs, is, tid)
 				}
 				err = nil
 
@@ -374,7 +374,7 @@ func (rt *loopRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interfa
 
 					// Execute block
 
-					_, err = rt.node.Children[1].Runtime.Eval(vs, is)
+					_, err = rt.node.Children[1].Runtime.Eval(vs, is, tid)
 				}
 
 				// Check for continue
@@ -421,8 +421,8 @@ func breakRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Run
 /*
 Eval evaluate this runtime component.
 */
-func (rt *breakRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
-	_, err := rt.baseRuntime.Eval(vs, is)
+func (rt *breakRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 		err = rt.erp.NewRuntimeError(util.ErrEndOfIteration, "", rt.node)
@@ -451,8 +451,8 @@ func continueRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.
 /*
 Eval evaluate this runtime component.
 */
-func (rt *continueRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
-	_, err := rt.baseRuntime.Eval(vs, is)
+func (rt *continueRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 		err = rt.erp.NewRuntimeError(util.ErrContinueIteration, "", rt.node)
@@ -481,7 +481,7 @@ func tryRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Runti
 /*
 Eval evaluate this runtime component.
 */
-func (rt *tryRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
+func (rt *tryRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
 	var res interface{}
 
 	evalExcept := func(errObj map[interface{}]interface{}, except *parser.ASTNode) bool {
@@ -493,7 +493,7 @@ func (rt *tryRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interfac
 
 			evs := vs.NewChild(scope.NameFromASTNode(except))
 
-			except.Children[0].Runtime.Eval(evs, is)
+			except.Children[0].Runtime.Eval(evs, is, tid)
 
 			ret = true
 
@@ -504,7 +504,7 @@ func (rt *tryRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interfac
 			evs := vs.NewChild(scope.NameFromASTNode(except))
 			evs.SetValue(except.Children[0].Token.Val, errObj)
 
-			except.Children[1].Runtime.Eval(evs, is)
+			except.Children[1].Runtime.Eval(evs, is, tid)
 
 			ret = true
 
@@ -515,7 +515,7 @@ func (rt *tryRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interfac
 				child := except.Children[i]
 
 				if !ret && child.Name == parser.NodeSTRING {
-					exceptError, evalErr := child.Runtime.Eval(vs, is)
+					exceptError, evalErr := child.Runtime.Eval(vs, is, tid)
 
 					// If we fail evaluating the string we panic as otherwise
 					// we would need to generate a new error while trying to handle another error
@@ -533,7 +533,7 @@ func (rt *tryRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interfac
 						evs.SetValue(errorVar, errObj)
 					}
 
-					child.Runtime.Eval(evs, is)
+					child.Runtime.Eval(evs, is, tid)
 				}
 			}
 		}
@@ -545,15 +545,15 @@ func (rt *tryRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interfac
 
 	if finally := rt.node.Children[len(rt.node.Children)-1]; finally.Name == parser.NodeFINALLY {
 		fvs := vs.NewChild(scope.NameFromASTNode(finally))
-		defer finally.Children[0].Runtime.Eval(fvs, is)
+		defer finally.Children[0].Runtime.Eval(fvs, is, tid)
 	}
 
-	_, err := rt.baseRuntime.Eval(vs, is)
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	if err == nil {
 		tvs := vs.NewChild(scope.NameFromASTNode(rt.node))
 
-		res, err = rt.node.Children[0].Runtime.Eval(tvs, is)
+		res, err = rt.node.Children[0].Runtime.Eval(tvs, is, tid)
 
 		// Evaluate except clauses
 

+ 12 - 12
interpreter/rt_value.go

@@ -50,8 +50,8 @@ func (rt *numberValueRuntime) Validate() error {
 /*
 Eval evaluate this runtime component.
 */
-func (rt *numberValueRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
-	_, err := rt.baseRuntime.Eval(vs, is)
+func (rt *numberValueRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	return rt.numValue, err
 }
@@ -73,8 +73,8 @@ func stringValueRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) pars
 /*
 Eval evaluate this runtime component.
 */
-func (rt *stringValueRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
-	_, err := rt.baseRuntime.Eval(vs, is)
+func (rt *stringValueRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	ret := rt.node.Token.Val
 
@@ -100,7 +100,7 @@ func (rt *stringValueRuntime) Eval(vs parser.Scope, is map[string]interface{}) (
 
 					res, ierr = ast.Runtime.Eval(
 						vs.NewChild(scope.NameFromASTNode(rt.node)),
-						make(map[string]interface{}))
+						make(map[string]interface{}), tid)
 
 					if ierr == nil {
 						replace = fmt.Sprint(res)
@@ -150,8 +150,8 @@ func mapValueRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.
 /*
 Eval evaluate this runtime component.
 */
-func (rt *mapValueRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
-	_, err := rt.baseRuntime.Eval(vs, is)
+func (rt *mapValueRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	m := make(map[interface{}]interface{})
 
@@ -160,8 +160,8 @@ func (rt *mapValueRuntime) Eval(vs parser.Scope, is map[string]interface{}) (int
 			var key, val interface{}
 
 			if err == nil {
-				if key, err = kvp.Children[0].Runtime.Eval(vs, is); err == nil {
-					if val, err = kvp.Children[1].Runtime.Eval(vs, is); err == nil {
+				if key, err = kvp.Children[0].Runtime.Eval(vs, is, tid); err == nil {
+					if val, err = kvp.Children[1].Runtime.Eval(vs, is, tid); err == nil {
 						m[key] = val
 					}
 				}
@@ -189,8 +189,8 @@ func listValueRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser
 /*
 Eval evaluate this runtime component.
 */
-func (rt *listValueRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
-	_, err := rt.baseRuntime.Eval(vs, is)
+func (rt *listValueRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
+	_, err := rt.baseRuntime.Eval(vs, is, tid)
 
 	var l []interface{}
 
@@ -198,7 +198,7 @@ func (rt *listValueRuntime) Eval(vs parser.Scope, is map[string]interface{}) (in
 		for _, item := range rt.node.Children {
 			if err == nil {
 				var val interface{}
-				if val, err = item.Runtime.Eval(vs, is); err == nil {
+				if val, err = item.Runtime.Eval(vs, is, tid); err == nil {
 					l = append(l, val)
 				}
 

+ 6 - 3
parser/runtime.go

@@ -33,12 +33,15 @@ type Runtime interface {
 
 	/*
 		Eval evaluate this runtime component. It gets passed the current variable
-		scope and the instance state.
+		scope an instance state and a thread ID.
 
 		The instance state is created per execution instance and can be used
-		for generator functions to store their current state.
+		for generator functions to store their current state. It gets replaced
+		by a new object in certain situations (e.g. a function call).
+
+		The thread ID can be used to identify a running process.
 	*/
-	Eval(Scope, map[string]interface{}) (interface{}, error)
+	Eval(Scope, map[string]interface{}, uint64) (interface{}, error)
 }
 
 /*

+ 1 - 1
stdlib/adapter.go

@@ -36,7 +36,7 @@ func NewECALFunctionAdapter(funcval reflect.Value, docstring string) *ECALFuncti
 Run executes this function.
 */
 func (ea *ECALFunctionAdapter) Run(instanceID string, vs parser.Scope,
-	is map[string]interface{}, args []interface{}) (ret interface{}, err error) {
+	is map[string]interface{}, tid uint64, args []interface{}) (ret interface{}, err error) {
 
 	defer func() {
 		if r := recover(); r != nil {

+ 1 - 1
stdlib/adapter_test.go

@@ -249,7 +249,7 @@ func TestECALFunctionAdapter(t *testing.T) {
 
 func runAdapterTest(afunc reflect.Value, args []interface{}) (interface{}, error) {
 	afuncEcal := &ECALFunctionAdapter{afunc, ""}
-	return afuncEcal.Run("test", scope.NewScope(""), make(map[string]interface{}), args)
+	return afuncEcal.Run("test", scope.NewScope(""), make(map[string]interface{}), 0, args)
 
 }
 

+ 1 - 1
util/types.go

@@ -41,7 +41,7 @@ type ECALFunction interface {
 		to store instance specific state (e.g. for iterator functions) and a list
 		of argument values which were passed to the function by the calling code.
 	*/
-	Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error)
+	Run(instanceID string, vs parser.Scope, is map[string]interface{}, tid uint64, args []interface{}) (interface{}, error)
 
 	/*
 	   DocString returns a descriptive text about this function.