10 #include "hashtable.h"
18 #define SHARED_MEMORY_DEFAULT (200 * ((size_t)1 << 20)) // 100mb for the shared memory
19 #define STACK_SIZE_DEFAULT (((size_t)1 << 20) * 20) // 20 mb out of the above 100 mb for my stack
21 struct fork_snapshotter {
22 /** @brief Pointer to the shared (non-snapshot) memory heap base
23 * (NOTE: this has size SHARED_MEMORY_DEFAULT - sizeof(*fork_snap)) */
24 void *mSharedMemoryBase;
26 /** @brief Pointer to the shared (non-snapshot) stack region */
29 /** @brief Size of the shared stack */
33 * @brief Stores the ID that we are attempting to roll back to
35 * Used in inter-process communication so that each process can
36 * determine whether or not to take over execution (w/ matching ID) or
37 * exit (we're rolling back even further). Dubiously marked 'volatile'
38 * to prevent compiler optimizations from messing with the
39 * inter-process behavior.
41 volatile snapshot_id mIDToRollback;
45 /** @brief Inter-process tracking of the next snapshot ID */
46 snapshot_id currSnapShotID;
49 static struct fork_snapshotter *fork_snap = NULL;
50 ucontext_t shared_ctxt;
53 * These variables are necessary because the stack is shared region and
54 * there exists a race between all processes executing the same function.
55 * To avoid the problem above, we require variables allocated in 'safe' regions.
56 * The bug was actually observed with the forkID, these variables below are
57 * used to indicate the various contexts to which to switch to.
59 * @private_ctxt: the context which is internal to the current process. Used
60 * for running the internal snapshot/rollback loop.
61 * @exit_ctxt: a special context used just for exiting from a process (so we
62 * can use swapcontext() instead of setcontext() + hacks)
63 * @snapshotid: it is a running counter for the various forked processes
64 * snapshotid. it is incremented and set in a persistently shared record
66 static ucontext_t private_ctxt;
67 static snapshot_id snapshotid = 0;
70 * @brief Create a new context, with a given stack and entry function
71 * @param ctxt The context structure to fill
72 * @param stack The stack to run the new context in
73 * @param stacksize The size of the stack
74 * @param func The entry point function for the context
76 static void create_context(ucontext_t *ctxt, void *stack, size_t stacksize,
80 ctxt->uc_stack.ss_sp = stack;
81 ctxt->uc_stack.ss_size = stacksize;
83 makecontext(ctxt, func, 0);
86 /** @brief An empty function, used for an "empty" context which just exits a
88 static void fork_exit()
93 static void createSharedMemory()
95 //step 1. create shared memory.
96 void *memMapBase = mmap(0, SHARED_MEMORY_DEFAULT + STACK_SIZE_DEFAULT, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
97 if (memMapBase == MAP_FAILED) {
102 //Setup snapshot record at top of free region
103 fork_snap = (struct fork_snapshotter *)memMapBase;
104 fork_snap->mSharedMemoryBase = (void *)((uintptr_t)memMapBase + sizeof(*fork_snap));
105 fork_snap->mStackBase = (void *)((uintptr_t)memMapBase + SHARED_MEMORY_DEFAULT);
106 fork_snap->mStackSize = STACK_SIZE_DEFAULT;
107 fork_snap->mIDToRollback = -1;
108 fork_snap->currSnapShotID = 0;
112 * Create a new mspace pointer for the non-snapshotting (i.e., inter-process
113 * shared) memory region. Only for fork-based snapshotting.
115 * @return The shared memory mspace
117 mspace create_shared_mspace()
120 createSharedMemory();
121 return create_mspace_with_base((void *)(fork_snap->mSharedMemoryBase), SHARED_MEMORY_DEFAULT - sizeof(*fork_snap), 1);
124 static void fork_snapshot_init(unsigned int numbackingpages,
125 unsigned int numsnapshots, unsigned int nummemoryregions,
126 unsigned int numheappages)
129 createSharedMemory();
131 model_snapshot_space = create_mspace(numheappages * PAGESIZE, 1);
134 volatile int modellock = 0;
136 static void fork_loop() {
137 /* switch back here when takesnapshot is called */
138 snapshotid = fork_snap->currSnapShotID;
139 if (model->params.nofork) {
140 setcontext(&shared_ctxt);
146 fork_snap->currSnapShotID = snapshotid + 1;
153 setcontext(&shared_ctxt);
155 DEBUG("parent PID: %d, child PID: %d, snapshot ID: %d\n",
156 getpid(), forkedID, snapshotid);
158 while (waitpid(forkedID, NULL, 0) < 0) {
159 /* waitpid() may be interrupted */
160 if (errno != EINTR) {
166 if (fork_snap->mIDToRollback != snapshotid)
172 static void fork_startExecution() {
173 /* switch to a new entryPoint context, on a new stack */
174 create_context(&private_ctxt, snapshot_calloc(STACK_SIZE_DEFAULT, 1), STACK_SIZE_DEFAULT, fork_loop);
177 static snapshot_id fork_take_snapshot() {
178 model_swapcontext(&shared_ctxt, &private_ctxt);
179 DEBUG("TAKESNAPSHOT RETURN\n");
180 fork_snap->mIDToRollback = -1;
184 static void fork_roll_back(snapshot_id theID)
187 fork_snap->mIDToRollback = theID;
192 * @brief Initializes the snapshot system
193 * @param entryPoint the function that should run the program.
195 void snapshot_system_init(unsigned int numbackingpages,
196 unsigned int numsnapshots, unsigned int nummemoryregions,
197 unsigned int numheappages)
199 fork_snapshot_init(numbackingpages, numsnapshots, nummemoryregions, numheappages);
202 void startExecution() {
203 fork_startExecution();
206 /** Takes a snapshot of memory.
207 * @return The snapshot identifier.
209 snapshot_id take_snapshot()
211 return fork_take_snapshot();
214 /** Rolls the memory state back to the given snapshot identifier.
215 * @param theID is the snapshot identifier to rollback to.
217 void snapshot_roll_back(snapshot_id theID)
219 fork_roll_back(theID);