blob: bf128a711260b1f1800680d7178c3360e2fe2ff6 [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2016, 2021 CEA LIST.
*
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Van Cam Pham <VanCam.PHAM@cea.fr>
*
*****************************************************************************/
package org.eclipse.papyrus.designer.transformation.languages.java.library.statemachine
import org.eclipse.uml2.uml.Class
import org.eclipse.uml2.uml.Region
import org.eclipse.uml2.uml.Transition
import org.eclipse.uml2.uml.Type
import static org.eclipse.papyrus.designer.transformation.library.statemachine.SMCodeGeneratorConstants.*
import static extension org.eclipse.papyrus.designer.transformation.library.statemachine.TransformationUtil.*
import static org.eclipse.papyrus.designer.transformation.languages.java.library.statemachine.SMJavaCodeGeneratorConstants.*
/**
* Java variant of concurrent execution. Incomplete
*/
class ConcurrencyGenerator {
protected extension CDefinitions cdefs;
SM2ClassesTransformationCore core
public Type threadStructType
Class superContext
new (SM2ClassesTransformationCore core) {
this.core = core
this.superContext = core.superContext
this.cdefs = core.cdefs
}
def createThreadBasedParallelism() {
// we should create wrapper functions (static) to create pthreads
// We should have: 1 wrapper for doActivity: DO_ACTIVITY_FOR_STATE
// 1 wrapper for regions: WRAPPER_REGION: similar to DO_ACTIVITY_FOR_STATE
// 1 wrapper for time event detection: WRAPPER_TIME_EVENT => run and wait as doActivity similar to DO_ACTIVITY_FOR_STATE
// 1 wrapper for change event detection: WRAPPER_TIME_EVENT => run, check value difference, write event similar to DO_ACTIVITY_FOR_STATE
/*
var threads = superContext.createOwnedAttribute(THREADS, ptTypes.pthread)
StereotypeUtil.apply(threads, Array)
UMLUtil.getStereotypeApplication(threads, Array).definition = '''[«STATE_MAX»]'''
var flags = superContext.createOwnedAttribute(FLAGS_ACTIVITY, core.boolType)
StereotypeUtil.apply(flags, Array)
UMLUtil.getStereotypeApplication(flags, Array).definition = '''[«STATE_MAX»]'''
var conds = superContext.createOwnedAttribute(CONDITIONS, ptTypes.pthreadCond)
StereotypeUtil.apply(conds, Array)
UMLUtil.getStereotypeApplication(conds, Array).definition = '''[«STATE_MAX»]'''
var mutexes = superContext.createOwnedAttribute(MUTEXES, ptTypes.pthreadMutex)
StereotypeUtil.apply(mutexes, Array)
UMLUtil.getStereotypeApplication(mutexes, Array).definition = '''[«STATE_MAX»]'''
threadStructType = core.threadStructType*/ //TODO
//threadStructType = superContext.createNestedClassifier(STRUCT_FOR_THREAD, UMLPackage.Literals.PRIMITIVE_TYPE)
// StereotypeUtil.apply(threadStructType, Typedef)
// UMLUtil.getStereotypeApplication(threadStructType, Typedef).definition = '''
// struct «STRUCT_FOR_THREAD» {
// void* ptr;
// int id; //id or index used to specify the corresponding function in a table
// char enter_mode; //used for specifying how a region is entered
// char func_type; // doActivity/enter region/exit region/time event/change event/ transition
// int duration; //in millisecond which is used for time events
// «STRUCT_FOR_THREAD»(«superContext.name»* ptr, int id, char enter_mode, char func_type, int duration): ptr(ptr), id(id), enter_mode(enter_mode), func_type(func_type), duration(duration){}
// «STRUCT_FOR_THREAD»(){}
// } '''
// TODO to place in a thread?
/*
var threadStructs = superContext.createOwnedAttribute(THREAD_STRUCTS, threadStructType)
StereotypeUtil.apply(threadStructs, Array)
UMLUtil.getStereotypeApplication(threadStructs, Array).definition = '''[«STATE_MAX»]'''
var threadFuncWrapper = superContext.createOwnedOperation(THREAD_FUNC_WRAPPER, null, null)
threadFuncWrapper.isStatic = true
var inParam = threadFuncWrapper.createOwnedParameter("data", core.voidType)
StereotypeUtil.apply(inParam, Ptr)
var outParam = threadFuncWrapper.createOwnedParameter("ret", core.voidType)
StereotypeUtil.apply(outParam, Ptr)
outParam.direction = ParameterDirectionKind.RETURN_LITERAL
core.createOpaqueBehavior(superContext, threadFuncWrapper, '''
«STRUCT_FOR_THREAD»* cptr = («STRUCT_FOR_THREAD»*)data;
«superContext.name»* ptr = («superContext.name»*) cptr->ptr;
switch(cptr->func_type) {
«IF core.doActivityList.size > 0»
case «THREAD_FUNC_DOACTIVITY_TYPE»:
ptr->«DO_CALL_ACTIVITY»(cptr->id);
break;
«ENDIF»
«IF core.timeEvents.size > 0»
case «THREAD_FUNC_TIMEEVENT_TYPE»:
ptr->«TIME_EVENT_LISTEN_FUNCTION»(«TE_INDEX»(cptr->id), cptr->duration);
break;
«ENDIF»
«IF core.orthogonalRegions.size > 0»
case «THREAD_FUNC_ENTER_REGION_TYPE»:
ptr->regionCall(cptr->id, cptr->enter_mode);
break;
case «THREAD_FUNC_EXIT_REGION_TYPE»:
ptr->exitRegionCall(cptr->id);
break;
«ENDIF»
«IF core.parallelTransitions.size > 0»
case «THREAD_FUNC_TRANSITION_TYPE»:
ptr->transitionCall(cptr->id);
break;
«ENDIF»
«IF core.changeEvents.size > 0»
case «THREAD_FUNC_CHANGEEVENT_TYPE»:
ptr->«CHANGE_EVENT_LISTEN_FUNCTION»(cptr->id);
break;
«ENDIF»
case «THREAD_FUNC_STATE_MACHINE_TYPE»:
ptr->«EVENT_DISPATCH»();
break;
default:
// do nothing
break;
}
return NULL;''')
//function pointer
core.fptr = superContext.createNestedClassifier(FPT_POINTER_FOR_TABLE, UMLPackage.Literals.PRIMITIVE_TYPE)
StereotypeUtil.apply(core.fptr, Typedef)
UMLUtil.getStereotypeApplication(core.fptr, Typedef).definition = '''
void («superContext.name»::*typeName)()'''
*/
/*var doActivityTable = superContext.createOwnedAttribute(DO_ACTIVITY_TABLE, core.fptr)
StereotypeUtil.apply(doActivityTable, Array)
UMLUtil.getStereotypeApplication(doActivityTable, Array).definition = '''[«STATE_MAX»]'''
*/
var doCallActivity = superContext.createOwnedOperation(DO_CALL_ACTIVITY, null, null)
doCallActivity.createOwnedParameter("id", core.intType)
core.createOpaqueBehavior(superContext, doCallActivity, '''
«FLAGS_ACTIVITY»[id] = false;
boolean exit = false;
while (!Thread.currentThread().isInterrupted()) {
synchronized(this) {
while (!flags[id]) {
try {
wait();
} catch (InterruptedException e) {
// interruption, exit loop
exit = true;
break;
}
}
if (exit) {
break;
}
states[id].doActivity.run();
«val hasTriggerlessTrans = core.states.filter[!it.composite && it.hasTriggerlessTransition].size > 0»
«IF hasTriggerlessTrans»
bool commitEvent = false;
«ENDIF»
if (flags[id]) {
«IF hasTriggerlessTrans»
commitEvent = true;
«ENDIF»
flags[id] = false;
}
notifyAll();
«IF hasTriggerlessTrans»
if (commitEvent) {
if(«FOR s:core.states.filter[!it.composite && it.hasTriggerlessTransition] SEPARATOR ' || '»id == «s.name.toUpperCase»_ID«ENDFOR») {
//processCompletionEvent();
«EVENT_QUEUE».push(EventPriority_t.PRIORITY_1, NULL, COMPLETIONEVENT_ID, statemachine.COMPLETION_EVENT, id);
}
}
«ENDIF»
}}
''')
var setFlag = superContext.createOwnedOperation(SET_FLAG, null, null)
setFlag.createOwnedParameter("id", core.intType)
setFlag.createOwnedParameter("func_type", core.threadFuncEnum)
setFlag.createOwnedParameter("value", core.boolType)
core.createOpaqueBehavior(superContext, setFlag, '''
//value = true => start activity
//value = false => stop activity
«IF core.timeEvents.size > 0»
if (func_type == «THREAD_FUNC_TIMEEVENT_TYPE») {
synchronized (this) {
timeEventFlags[«TE_INDEX»(id)] = value;
}
return;
}
«ENDIF»
if (func_type == «THREAD_FUNC_DOACTIVITY_TYPE») {
«IF core.doActivityList.size > 0»
if («STATE_ARRAY_ATTRIBUTE»[id].doActivity != doActivity_dft) {
// state has doActivity => if value = true => should
synchronized (this) {
if (value) {
flags[id] = true;
} else {
// wait until doActivity exits
while (flags[id]) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
notifyAll();
}
}
else {
«IF core.states.filter[!it.composite && it.hasTriggerlessTransition].size > 0»
// push completion event
if (value) {
if(«FOR s:core.states.filter[!it.composite && it.hasTriggerlessTransition] SEPARATOR ' || '»id == «s.name.toUpperCase»_ID«ENDFOR») {
«EVENT_QUEUE».push(statemachineEventPriority_t.PRIORITY_1, Ne::EventPriority_t.PRIORITY_TIONEVENT_ID, statemachine::COMPLETION_EVENT, id);
}
}
«ENDIF»
}
return;
«ELSE»
«IF core.states.filter[!it.composite && it.hasTriggerlessTransition].size > 0»
// push completion event
if (value) {
if («FOR s:core.states.filter[!it.composite && it.hasTriggerlessTransition] SEPARATOR ' || '»id == «s.name.toUpperCase»_ID«ENDFOR») {
«EVENT_QUEUE».push(statemachineEventPriority_t.PRIORITY_1, NULL, COMPLETIONEVENT_ID, COMPLETION_EVENT, id);
}
return;
}
«ENDIF»
«ENDIF»
}
''')
//create utility fork and join for regions
//fork has two parameters: threadId and data (thread struct)
// var forkOp = superContext.createOwnedOperation(FORK_NAME, null, null)
// var threadIdParam = forkOp.createOwnedParameter("tId", pThreadType)
// StereotypeUtil.apply(threadIdParam, Ptr)
// var data = forkOp.createOwnedParameter("data", core.voidType)
// StereotypeUtil.apply(data, Ptr)
// core.createOpaqueBehavior(superContext, forkOp, '''
// pthread_create(tId, NULL, &«superContext.name»::«THREAD_FUNC_WRAPPER», data);''')
//
// var joinOp = superContext.createOwnedOperation(JOIN_NAME, null, null)
// joinOp.createOwnedParameter("tId", pThreadType)
// core.createOpaqueBehavior(superContext, joinOp, '''
// pthread_join(tId, NULL);''')
new TimeEventTransformation(core).createTimeEvents
createRegionParallel
}
def createRegionParallel() {
/*
if (core.orthogonalRegions.empty)
return
var regionFptr = superContext.createNestedClassifier(REGION_FUNCTION_PTR, UMLPackage.Literals.PRIMITIVE_TYPE)
StereotypeUtil.apply(regionFptr, Typedef)
UMLUtil.getStereotypeApplication(regionFptr, Typedef).definition = '''
void («superContext.name»::*typeName)(char enter_mode)'''
var regionTable = superContext.createOwnedAttribute(REGION_TABLE, regionFptr)
StereotypeUtil.apply(regionTable, Array)
UMLUtil.getStereotypeApplication(regionTable, Array).definition = '''[«core.orthogonalRegions.size»]'''
var regionCallOp = superContext.createOwnedOperation("regionCall", null, null)
regionCallOp.createOwnedParameter("id", core.intType)
regionCallOp.createOwnedParameter("enter_mode", core.charType)
core.createOpaqueBehavior(superContext, regionCallOp, '''
(this->*«REGION_TABLE»[id])(enter_mode);''')
var regionExitTable = superContext.createOwnedAttribute(REGION_TABLE_EXIT, core.fptr)
StereotypeUtil.apply(regionExitTable, Array)
UMLUtil.getStereotypeApplication(regionExitTable, Array).definition = '''[«core.orthogonalRegions.size»]'''
var regionCallExitOp = superContext.createOwnedOperation("exitRegionCall", null, null)
regionCallExitOp.createOwnedParameter("id", core.charType)
core.createOpaqueBehavior(superContext, regionCallExitOp, '''
(this->*«REGION_TABLE_EXIT»[id])();''')*/ //TODO
}
def createConcurrencyForTransitions() {
/*
if (core.parallelTransitions.empty) {
return
}
for(var i = 0; i < core.parallelTransitions.size; i++) {
core.appendImport('''
#define «core.parallelTransitions.get(i).parallelTransitionId» («i»)''')
var op = superContext.createOwnedOperation(core.parallelTransitions.get(i).parallelTransitionMethodName, null, null)
core.createOpaqueBehavior(superContext, op, '''
«TransformationUtil.getTransitionEffect(core.parallelTransitions.get(i))»''')
}
var transitionTable = superContext.createOwnedAttribute(PARALLEL_TRANSITION_TABLE, core.fptr)
StereotypeUtil.apply(transitionTable, Array)
UMLUtil.getStereotypeApplication(transitionTable, Array).definition = '''[«core.parallelTransitions.size»]'''
var transitionCallOp = superContext.createOwnedOperation("transitionCall", null, null)
transitionCallOp.createOwnedParameter("id", core.intType)
core.createOpaqueBehavior(superContext, transitionCallOp, '''
(this->*«PARALLEL_TRANSITION_TABLE»[id])();''') */ //TODO
}
def parallelTransitionMethodName(Transition t) {
return '''paralleTransition«core.parallelTransitions.indexOf(t)»'''
}
def parallelTransitionId(Transition t) {
return '''PARALLEL_TRANSITION_ID_«core.parallelTransitions.indexOf(t)»'''
}
def generateForkCall(Region r, boolean isEnter, String enteringMode) {
var paramThreadName = '''«r.state.name»_«r.name»_enter_thread'''
var threadStructParam = '''«r.state.name»_«r.name»_enter_thread_struct'''
if (!isEnter) {
paramThreadName = '''«r.state.name»_«r.name»_exit_thread'''
threadStructParam = '''«r.state.name»_«r.name»_exit_thread_struct'''
}
// «superContext.name»* ptr;
// int id; //id or index used to specify the corresponding function in a table
// char enter_mode; //used for specifying how a region is entered
// char func_type; // doActivity/enter region/exit region/time event/change event/ transition
// int duration; //in millisecond which is used for time events
return '''
pthread_t «paramThreadName»;
«STRUCT_FOR_THREAD» «threadStructParam»(this, «core.getRegionMacroId(r)», «enteringMode», «IF isEnter»«THREAD_FUNC_ENTER_REGION_TYPE»«ELSE»«THREAD_FUNC_EXIT_REGION_TYPE»«ENDIF», 0);
«superContext.name»_THREAD_CREATE(«paramThreadName», «threadStructParam»)
'''
}
def generateJoinCall(Region r, boolean isEnter) {
var paramThreadName = '''«r.state.name»_«r.name»_enter_thread'''
if (!isEnter) {
paramThreadName = '''«r.state.name»_«r.name»_exit_thread'''
}
return '''«JOIN_NAME»(«paramThreadName», NULL);'''
}
}