+ volatile snapshot_id mIDToRollback;
+
+
+
+ /** @brief Inter-process tracking of the next snapshot ID */
+ snapshot_id currSnapShotID;
+};
+
+static struct fork_snapshotter *fork_snap = NULL;
+ucontext_t shared_ctxt;
+
+/** @statics
+ * These variables are necessary because the stack is shared region and
+ * there exists a race between all processes executing the same function.
+ * To avoid the problem above, we require variables allocated in 'safe' regions.
+ * The bug was actually observed with the forkID, these variables below are
+ * used to indicate the various contexts to which to switch to.
+ *
+ * @private_ctxt: the context which is internal to the current process. Used
+ * for running the internal snapshot/rollback loop.
+ * @exit_ctxt: a special context used just for exiting from a process (so we
+ * can use swapcontext() instead of setcontext() + hacks)
+ * @snapshotid: it is a running counter for the various forked processes
+ * snapshotid. it is incremented and set in a persistently shared record
+ */
+static ucontext_t private_ctxt;
+static ucontext_t exit_ctxt;
+static snapshot_id snapshotid = 0;
+
+/**
+ * @brief Create a new context, with a given stack and entry function
+ * @param ctxt The context structure to fill
+ * @param stack The stack to run the new context in
+ * @param stacksize The size of the stack
+ * @param func The entry point function for the context
+ */
+static void create_context(ucontext_t *ctxt, void *stack, size_t stacksize,
+ void (*func)(void))
+{
+ getcontext(ctxt);
+ ctxt->uc_stack.ss_sp = stack;
+ ctxt->uc_stack.ss_size = stacksize;
+ ctxt->uc_link = NULL;
+ makecontext(ctxt, func, 0);
+}
+
+/** @brief An empty function, used for an "empty" context which just exits a
+ * process */
+static void fork_exit()
+{
+ _Exit(EXIT_SUCCESS);
+}
+
+static void createSharedMemory()
+{
+ //step 1. create shared memory.
+ void *memMapBase = mmap(0, SHARED_MEMORY_DEFAULT + STACK_SIZE_DEFAULT, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
+ if (memMapBase == MAP_FAILED) {
+ perror("mmap");
+ exit(EXIT_FAILURE);
+ }
+
+ //Setup snapshot record at top of free region
+ fork_snap = (struct fork_snapshotter *)memMapBase;
+ fork_snap->mSharedMemoryBase = (void *)((uintptr_t)memMapBase + sizeof(*fork_snap));
+ fork_snap->mStackBase = (void *)((uintptr_t)memMapBase + SHARED_MEMORY_DEFAULT);
+ fork_snap->mStackSize = STACK_SIZE_DEFAULT;
+ fork_snap->mIDToRollback = -1;
+ fork_snap->currSnapShotID = 0;
+}
+
+/**
+ * Create a new mspace pointer for the non-snapshotting (i.e., inter-process
+ * shared) memory region. Only for fork-based snapshotting.
+ *
+ * @return The shared memory mspace
+ */
+mspace create_shared_mspace()
+{
+ if (!fork_snap)
+ createSharedMemory();
+ return create_mspace_with_base((void *)(fork_snap->mSharedMemoryBase), SHARED_MEMORY_DEFAULT - sizeof(*fork_snap), 1);
+}
+
+static void fork_snapshot_init(unsigned int numbackingpages,
+ unsigned int numsnapshots, unsigned int nummemoryregions,
+ unsigned int numheappages)
+{
+ if (!fork_snap)
+ createSharedMemory();
+
+ model_snapshot_space = create_mspace(numheappages * PAGESIZE, 1);
+}
+
+volatile int modellock = 0;
+
+static void fork_loop() {
+ /* switch back here when takesnapshot is called */
+ snapshotid = fork_snap->currSnapShotID;
+ if (model->params.nofork) {
+ setcontext(&shared_ctxt);
+ _Exit(EXIT_SUCCESS);
+ }
+
+ while (true) {
+ pid_t forkedID;
+ fork_snap->currSnapShotID = snapshotid + 1;
+
+ modellock = 1;
+ forkedID = fork();
+ modellock = 0;
+
+ if (0 == forkedID) {
+ setcontext(&shared_ctxt);
+ } else {
+ DEBUG("parent PID: %d, child PID: %d, snapshot ID: %d\n",
+ getpid(), forkedID, snapshotid);
+
+ while (waitpid(forkedID, NULL, 0) < 0) {
+ /* waitpid() may be interrupted */
+ if (errno != EINTR) {
+ perror("waitpid");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (fork_snap->mIDToRollback != snapshotid)
+ _Exit(EXIT_SUCCESS);
+ }
+ }
+}
+
+static void fork_startExecution(ucontext_t *context, VoidFuncPtr entryPoint) {
+ /* setup an "exiting" context */
+ int exit_stack_size = 256;
+ create_context(&exit_ctxt, snapshot_calloc(exit_stack_size, 1), exit_stack_size, fork_exit);
+
+ /* setup the system context */
+ create_context(context, fork_snap->mStackBase, STACK_SIZE_DEFAULT, entryPoint);
+ /* switch to a new entryPoint context, on a new stack */
+ create_context(&private_ctxt, snapshot_calloc(STACK_SIZE_DEFAULT, 1), STACK_SIZE_DEFAULT, fork_loop);
+}
+
+static snapshot_id fork_take_snapshot() {
+ model_swapcontext(&shared_ctxt, &private_ctxt);
+ DEBUG("TAKESNAPSHOT RETURN\n");
+ return snapshotid;
+}
+
+static void fork_roll_back(snapshot_id theID)
+{
+ DEBUG("Rollback\n");
+ fork_snap->mIDToRollback = theID;
+ model_swapcontext(model->get_system_context(), &exit_ctxt);
+ fork_snap->mIDToRollback = -1;
+}
+
+#endif /* !USE_MPROTECT_SNAPSHOT */
+
+/**
+ * @brief Initializes the snapshot system
+ * @param entryPoint the function that should run the program.
+ */
+void snapshot_system_init(unsigned int numbackingpages,
+ unsigned int numsnapshots, unsigned int nummemoryregions,
+ unsigned int numheappages)
+{
+#if USE_MPROTECT_SNAPSHOT
+ mprot_snapshot_init(numbackingpages, numsnapshots, nummemoryregions, numheappages);
+#else
+ fork_snapshot_init(numbackingpages, numsnapshots, nummemoryregions, numheappages);
+#endif
+}
+
+void startExecution(ucontext_t *context, VoidFuncPtr entryPoint)
+{
+#if USE_MPROTECT_SNAPSHOT
+ mprot_startExecution(context, entryPoint);
+#else
+ fork_startExecution(context, entryPoint);