Sources of Randomness
In order to circumvent nondeterministic execution when running in simulation mode, the Java bytecode of the system is instrumented using the Javaassist toolkit to intercept a number of method calls and replace them with code that exhibits similar, but deterministic behaviour.
The advantage of using bytecode instrumentation for whole-system simulation is that in order to execute a system in simulation there is no need to change any of its source code. The implication of this fact is that we can simulate not only the code of the system under development, but also any third-party libraries that it might use. The only limitation of this approach is when a third-party library invokes native code. Allowing the execution to “escape” the managed environment of the JVM into native code means that we loose the guarantee of deterministic execution.
As stated before, the sources of randomness that we consider in simulation include:
- system time
- random generators
- thread creation/execution
- network, e.g. latency, packet loss
- input/output sources
System time
The two sources of system time that we consider are the Java system calls: System.currentTimeMillis()
and System.nanoTime()
.
These two methods will be instrumented to point to simulation time, instead. Since the granulatiry of simulation time is only milliseconds, the System.nanoTime()
is a simple multiple of System.currentTimeMillis()
in simulation.
Random Generators
The random generators we consider are Random
, SecureRandom
, and UUID
generation.
Currently, SecureRandom
type generators are simply not allowed in simulations.
When using new Random()
(the no argument constructor), the call is instrumented to new Random(0)
, in order to be deterministic.
When using UUID.randomUUID()
the call is instrumented as two calls to the Random.nextLong()
method on the simulation’s global random generator (which is controlled by the simulation seed).
Thread Creation
Thread interleaving creates nondeterminism, which we want to avoid during simulation and thus, we instrument calls to Thread.start()
and Thread.sleep()
. All attempts to create threads are intercepted and the simulation halts with an error informing the user that deterministic execution cannot be guaranteed. Kompics protocol components are typically reactive and do not need to spawn threads of their own, so they lend themselves well to simulation.
In some cases, especially third party libraries, threads are created, but they might not interact with component logic. If this is the case, we can add that class to the exception list (see section Instrumentation Exceptions) to prevent it from getting instrumented.
Network
Randomness induced by network parameters, such as latency and network loss, are modeled using the SimulationScenario
and NetworkModel
abstraction.
Input/Output sources
Currently, there is no warning/error for using I/O Sources, but it should be kept in mind that these might introduce uncertainty into the simulation.
Instrumentation Exceptions
You can add classes to the instrumentation exception list by adding the following lines to your configuration file:
instrumentation.exceptions = [
"a.b.c.MyClass",
"d.e.f.MyOtherClass"
]