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 ucontext_t exit_ctxt;
68 static snapshot_id snapshotid = 0;
71 * @brief Create a new context, with a given stack and entry function
72 * @param ctxt The context structure to fill
73 * @param stack The stack to run the new context in
74 * @param stacksize The size of the stack
75 * @param func The entry point function for the context
77 static void create_context(ucontext_t *ctxt, void *stack, size_t stacksize,
81 ctxt->uc_stack.ss_sp = stack;
82 ctxt->uc_stack.ss_size = stacksize;
84 makecontext(ctxt, func, 0);
87 /** @brief An empty function, used for an "empty" context which just exits a
89 static void fork_exit()
94 static void createSharedMemory()
96 //step 1. create shared memory.
97 void *memMapBase = mmap(0, SHARED_MEMORY_DEFAULT + STACK_SIZE_DEFAULT, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
98 if (memMapBase == MAP_FAILED) {
103 //Setup snapshot record at top of free region
104 fork_snap = (struct fork_snapshotter *)memMapBase;
105 fork_snap->mSharedMemoryBase = (void *)((uintptr_t)memMapBase + sizeof(*fork_snap));
106 fork_snap->mStackBase = (void *)((uintptr_t)memMapBase + SHARED_MEMORY_DEFAULT);
107 fork_snap->mStackSize = STACK_SIZE_DEFAULT;
108 fork_snap->mIDToRollback = -1;
109 fork_snap->currSnapShotID = 0;
113 * Create a new mspace pointer for the non-snapshotting (i.e., inter-process
114 * shared) memory region. Only for fork-based snapshotting.
116 * @return The shared memory mspace
118 mspace create_shared_mspace()
121 createSharedMemory();
122 return create_mspace_with_base((void *)(fork_snap->mSharedMemoryBase), SHARED_MEMORY_DEFAULT - sizeof(*fork_snap), 1);
125 static void fork_snapshot_init(unsigned int numbackingpages,
126 unsigned int numsnapshots, unsigned int nummemoryregions,
127 unsigned int numheappages)
130 createSharedMemory();
132 model_snapshot_space = create_mspace(numheappages * PAGESIZE, 1);
135 volatile int modellock = 0;
137 static void fork_loop() {
138 /* switch back here when takesnapshot is called */
139 snapshotid = fork_snap->currSnapShotID;
140 if (model->params.nofork) {
141 setcontext(&shared_ctxt);
147 fork_snap->currSnapShotID = snapshotid + 1;
154 setcontext(&shared_ctxt);
156 DEBUG("parent PID: %d, child PID: %d, snapshot ID: %d\n",
157 getpid(), forkedID, snapshotid);
159 while (waitpid(forkedID, NULL, 0) < 0) {
160 /* waitpid() may be interrupted */
161 if (errno != EINTR) {
167 if (fork_snap->mIDToRollback != snapshotid)
173 static void fork_startExecution(ucontext_t *context, VoidFuncPtr entryPoint) {
174 /* setup an "exiting" context */
175 int exit_stack_size = 256;
176 create_context(&exit_ctxt, snapshot_calloc(exit_stack_size, 1), exit_stack_size, fork_exit);
178 /* setup the system context */
179 create_context(context, fork_snap->mStackBase, STACK_SIZE_DEFAULT, entryPoint);
180 /* switch to a new entryPoint context, on a new stack */
181 create_context(&private_ctxt, snapshot_calloc(STACK_SIZE_DEFAULT, 1), STACK_SIZE_DEFAULT, fork_loop);
184 static snapshot_id fork_take_snapshot() {
185 model_swapcontext(&shared_ctxt, &private_ctxt);
186 DEBUG("TAKESNAPSHOT RETURN\n");
190 static void fork_roll_back(snapshot_id theID)
193 fork_snap->mIDToRollback = theID;
194 model_swapcontext(model->get_system_context(), &exit_ctxt);
195 fork_snap->mIDToRollback = -1;
199 * @brief Initializes the snapshot system
200 * @param entryPoint the function that should run the program.
202 void snapshot_system_init(unsigned int numbackingpages,
203 unsigned int numsnapshots, unsigned int nummemoryregions,
204 unsigned int numheappages)
206 fork_snapshot_init(numbackingpages, numsnapshots, nummemoryregions, numheappages);
209 void startExecution(ucontext_t *context, VoidFuncPtr entryPoint)
211 fork_startExecution(context, entryPoint);
214 /** Takes a snapshot of memory.
215 * @return The snapshot identifier.
217 snapshot_id take_snapshot()
219 return fork_take_snapshot();
222 /** Rolls the memory state back to the given snapshot identifier.
223 * @param theID is the snapshot identifier to rollback to.
225 void snapshot_roll_back(snapshot_id theID)
227 fork_roll_back(theID);