...
 
Commits (2)
...@@ -189,6 +189,21 @@ The following environment variables control Awlsim's basic behavior: ...@@ -189,6 +189,21 @@ The following environment variables control Awlsim's basic behavior:
* `AWLSIM_COVERAGE`<br /> * `AWLSIM_COVERAGE`<br />
`=DATAFILE` Enable code coverage tracing.<br /> `=DATAFILE` Enable code coverage tracing.<br />
* `AWLSIM_GCMODE`<br />
`=realtime` Enable manual garbage collection, if realtime scheduling is enabled. (default)<br />
`=auto` Always use automatic garbage collection.<br />
`=manual` Always use manual garbage collection.<br />
* `AWLSIM_GCTHRES`<br />
`=700,1,1`<br />
A comma separated string with up to 3 integers.<br />
Each integer corresponding to the Python garbage collector generation 0 to 2 thresholds for manual garbage collection.<br />
A threshold value of 0 disables garbage collection. (not recommended)<br />
* `AWLSIM_GCCYCLE`<br />
`=64`<br />
The number of OB1 cycles it takes to trigger a manual garbage collection.<br />
## Building Awlsim ## Building Awlsim
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# #
# AWL simulator - Environment variables # AWL simulator - Environment variables
# #
# Copyright 2017-2018 Michael Buesch <m@bues.ch> # Copyright 2017-2019 Michael Buesch <m@bues.ch>
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
...@@ -27,6 +27,7 @@ from awlsim.common.util import * ...@@ -27,6 +27,7 @@ from awlsim.common.util import *
from awlsim.common.exceptions import * from awlsim.common.exceptions import *
import os import os
import gc
__all__ = [ __all__ = [
...@@ -141,3 +142,55 @@ class AwlSimEnv(object): ...@@ -141,3 +142,55 @@ class AwlSimEnv(object):
except ValueError as e: except ValueError as e:
pass pass
return None return None
GCMODE_RT = "realtime" # Manual GC, if realtime scheduling
GCMODE_AUTO = "auto" # Automatic GC
GCMODE_MANUAL = "manual" # Manual GC
@classmethod
def getGcMode(cls):
"""Get AWLSIM_GCMODE.
Returns one of the GCMODE_... constants.
"""
gcModeStr = cls.__getVar("GCMODE", "").lower().strip()
if gcModeStr == cls.GCMODE_RT:
return cls.GCMODE_RT
if gcModeStr == cls.GCMODE_AUTO:
return cls.GCMODE_AUTO
if gcModeStr == cls.GCMODE_MANUAL:
return cls.GCMODE_MANUAL
return cls.GCMODE_RT
@classmethod
def getGcThreshold(cls, generation):
"""Get AWLSIM_GCTHRES.
AWLSIM_GCTHRES is a comma separated string with up to 3 integers.
Each integer corresponding to the generation 0 to 2 thresholds.
Returns the garbage collector threshold for the selected generation.
"""
thresStr = cls.__getVar("GCTHRES", "")
thres = thresStr.split(",")
assert(generation in (0, 1, 2))
try:
return clamp(int(thres[generation]),
0, 0x7FFFFFFF)
except (ValueError, IndexError) as e:
if generation == 0:
gc_get_threshold = getattr(gc, "get_threshold", None)
if gc_get_threshold:
return gc_get_threshold()[0]
return 700
return 1
@classmethod
def getGcCycle(cls):
"""Get AWLSIM_GCCYCLE.
AWLSIM_GCCYCLE is the number of OB1 cycles it takes to trigger
a manual garbage collection.
Returns an integer.
"""
cycStr = cls.__getVar("GCCYCLE", "")
try:
return clamp(int(cycStr), 1, 0xFFFF)
except ValueError as e:
return 64
...@@ -11,9 +11,14 @@ cdef class AwlSimServer(object): ...@@ -11,9 +11,14 @@ cdef class AwlSimServer(object):
cdef public _Bool __affinityEnabled cdef public _Bool __affinityEnabled
cdef public _Bool __rtSchedEnabled cdef public _Bool __rtSchedEnabled
cdef public object __os_sched_yield cdef public object __os_sched_yield
cdef public object __gc_enable cdef public _Bool __gcManual
cdef public object __gc_disable cdef public uint16_t __gcTriggerCounter
cdef public uint16_t __gcTriggerThreshold
cdef public int32_t __gcGen0Threshold
cdef public int32_t __gcGen1Threshold
cdef public int32_t __gcGen2Threshold
cdef public object __gc_collect cdef public object __gc_collect
cdef public object __gc_get_count
cdef public list __emptyList cdef public list __emptyList
cdef public _Bool __startupDone cdef public _Bool __startupDone
cdef public int32_t __state cdef public int32_t __state
......
...@@ -233,9 +233,14 @@ class AwlSimServer(object): #+cdef ...@@ -233,9 +233,14 @@ class AwlSimServer(object): #+cdef
self.__affinityEnabled = False self.__affinityEnabled = False
self.__rtSchedEnabled = False self.__rtSchedEnabled = False
self.__os_sched_yield = getattr(os, "sched_yield", None) self.__os_sched_yield = getattr(os, "sched_yield", None)
self.__gc_enable = gc.enable self.__gcManual = False
self.__gc_disable = gc.disable self.__gcTriggerCounter = 0
self.__gc_collect = gc.collect self.__gcTriggerThreshold = AwlSimEnv.getGcCycle()
self.__gcGen0Threshold = AwlSimEnv.getGcThreshold(0)
self.__gcGen1Threshold = AwlSimEnv.getGcThreshold(1)
self.__gcGen2Threshold = AwlSimEnv.getGcThreshold(2)
self.__gc_collect = getattr(gc, "collect", None)
self.__gc_get_count = getattr(gc, "get_count", None)
self.__emptyList = [] self.__emptyList = []
self.__startupDone = False self.__startupDone = False
self.__state = -1 self.__state = -1
...@@ -422,25 +427,39 @@ class AwlSimServer(object): #+cdef ...@@ -422,25 +427,39 @@ class AwlSimServer(object): #+cdef
"os.sched_setparam/os.sched_getscheduler " "os.sched_setparam/os.sched_getscheduler "
"is not available.") "is not available.")
# On realtime scheduling switch to manual garbage collection.
if self.__rtSchedEnabled:
self.__gc_disable()
else:
self.__gc_enable()
def __yieldHostCPU(self): #@nocy def __yieldHostCPU(self): #@nocy
#@cy cdef void __yieldHostCPU(self): #@cy cdef void __yieldHostCPU(self):
#@cy cdef uint16_t trig
# Run a garbage collection now to maintain realtime requirements. #@cy cdef int32_t gen0
self.__gc_collect() #@cy cdef int32_t thres0
#@cy cdef int32_t gen1
#@cy cdef int32_t thres1
#@cy cdef int32_t gen2
#@cy cdef int32_t thres2
# If automatic garbage collection is disabled,
# run a manual collection now.
if self.__gcManual:
trig = (self.__gcTriggerCounter + 1) & 0xFFFF #+suffix-u
if trig >= self.__gcTriggerThreshold:
trig = 0
assert(not gc.isenabled()) #@nocy
gen0, gen1, gen2 = self.__gc_get_count()
thres0 = self.__gcGen0Threshold
thres1 = self.__gcGen1Threshold
thres2 = self.__gcGen2Threshold
if gen2 >= thres2 and thres2 > 0:
self.__gc_collect(2)
elif gen1 >= thres1 and thres1 > 0:
self.__gc_collect(1)
elif gen0 >= thres0 and thres0 > 0:
self.__gc_collect(0)
self.__gcTriggerCounter = trig
if self.__rtSchedEnabled: if self.__rtSchedEnabled:
# We are running under realtime scheduling conditions. # We are running under realtime scheduling conditions.
# We should yield now. # We should yield now.
# Re-enable to garbage collector.
self.__gc_enable()
# On Posix + Cython call the system sched_yield directly. # On Posix + Cython call the system sched_yield directly.
# with nogil: #@cy-posix # with nogil: #@cy-posix
# sched_yield() #@cy-posix # sched_yield() #@cy-posix
...@@ -450,9 +469,6 @@ class AwlSimServer(object): #+cdef ...@@ -450,9 +469,6 @@ class AwlSimServer(object): #+cdef
if self.__os_sched_yield: #@nocy if self.__os_sched_yield: #@nocy
self.__os_sched_yield() #@nocy self.__os_sched_yield() #@nocy
# Disable to garbage collector.
self.__gc_disable()
def getRunState(self): def getRunState(self):
return self.__state return self.__state
...@@ -500,6 +516,30 @@ class AwlSimServer(object): #+cdef ...@@ -500,6 +516,30 @@ class AwlSimServer(object): #+cdef
self.__setSched(False) self.__setSched(False)
self.__setAffinity(False) self.__setAffinity(False)
# Select garbage collection mode.
if self.__gc_collect and self.__gc_get_count:
# If we are in RUN state with realtime scheduling,
# use manual garbage collection.
gcMode = AwlSimEnv.getGcMode()
wantManual = (gcMode == AwlSimEnv.GCMODE_MANUAL or
(gcMode == AwlSimEnv.GCMODE_RT and
self.__rtSchedEnabled))
if runstate == self.STATE_RUN and wantManual:
# Manual GC
gc.disable()
self.__gcManual = True
self.__gcTriggerCounter = 0
printVerbose("Switched to MANUAL garbage collection.")
else:
# Automatic GC
gc.enable()
self.__gcManual = False
printVerbose("Switched to AUTO garbage collection.")
else:
# Manual GC control is not available.
self.__gcManual = False
printVerbose("Switched to AUTO garbage collection.")
self.__state = runstate self.__state = runstate
# Make a shortcut variable for RUN # Make a shortcut variable for RUN
self.__running = bool(runstate == self.STATE_RUN) self.__running = bool(runstate == self.STATE_RUN)
......