The ROme OpTimistic Simulator  2.0.0
A General-Purpose Multithreaded Parallel/Distributed Simulation Platform
abm_layer.c
Go to the documentation of this file.
1 
27 #include <ROOT-Sim.h>
28 #include <lib/abm_layer.h>
29 
30 #include <core/init.h>
31 #include <scheduler/scheduler.h>
32 #include <lib/topology.h>
33 #include <datatypes/array.h>
34 #include <datatypes/hash_map.h>
35 
36 #define ACTION_START INIT
37 
38 #define retrieve_agent(agent_id) ({ \
39  struct _agent_abm_t *__ret = hash_map_lookup(current->region->agents_table, agent_id); \
40  if(unlikely(!__ret)) \
41  rootsim_error(true, "Looking for non existing agent id!"); \
42  assert(agent_id == __ret->key); \
43  __ret; \
44 })
45 
46 struct _visit_abm_t{
47  unsigned region;
48  unsigned action;
49  simtime_t time;
50 };
51 
52 struct _agent_abm_t {
53  unsigned long long key;
54  unsigned user_data_size;
55  char *user_data;
56  simtime_t leave_time;
57  rootsim_array(struct _visit_abm_t) future;
58  rootsim_array(struct _visit_abm_t) past;
59 };
60 
61 struct _region_abm_t {
62  rootsim_hash_map(struct _agent_abm_t) agents_table;
63  unsigned long long next_mark;
64  unsigned published_data_offset;
65  unsigned char *tracked_data;
66  unsigned chkp_size;
67  struct _n_info_t{
68  unsigned data_offset;
69  unsigned int src_lp;
70  } neighbours_info[];
71 };
72 
73 struct _leave_evt{
74  unsigned long long key;
75  unsigned leave_code;
76 };
77 
78 static inline unsigned long long get_agent_mark(region_abm_t *region){
79  unsigned long long ret = region->next_mark;
80  region->next_mark += RegionsCount();
81  return ret;
82 }
83 
84 
90 static unsigned agent_dump_size(const struct _agent_abm_t *agent) {
91  return (unsigned)(sizeof(agent->key) + sizeof(agent->user_data_size) + agent->user_data_size
92  + array_dump_size(agent->future) + abm_settings.keep_history * array_dump_size(agent->past));
93 }
94 
95 
103 static struct _agent_abm_t* agent_from_buffer(const unsigned char* event_content, unsigned event_size){
104  // keep track of original pointer
105  const unsigned char *buffer = event_content;
106  // allocate the memory for the visiting agent
107  struct _agent_abm_t *agent = hash_map_reserve_elem(current->region->agents_table, *((const unsigned long long *)event_content));
108  agent->leave_time = -1.0;
109  // copy uuid and user data size
110  memcpy(agent, buffer, sizeof(agent->user_data_size) + sizeof(agent->key));
111  buffer += sizeof(agent->user_data_size) + sizeof(agent->key);
112  // copy user data
113  agent->user_data = rsalloc(agent->user_data_size);
114  memcpy(agent->user_data, buffer, agent->user_data_size);
115  buffer += agent->user_data_size;
116  // load the arrays
117  array_load(agent->future, buffer);
118  if(abm_settings.keep_history)
119  array_load(agent->past, buffer);
120  else
121  memset(&agent->past, 0, sizeof(agent->past));
122 
123  //sanity check
124  assert(buffer == event_content + event_size);
125  return agent;
126 }
127 
128 
136 static void agent_to_buffer(struct _agent_abm_t *agent, unsigned char* buffer){
137  // we use buffer as a mobile pointer so we need to save the original value
138  unsigned char *to_send = buffer;
139  // copy relevant fields
140  memcpy(buffer, agent, sizeof(agent->user_data_size) + sizeof(agent->key));
141  buffer += sizeof(agent->user_data_size) + sizeof(agent->key);
142  memcpy(buffer, agent->user_data, agent->user_data_size);
143  buffer += agent->user_data_size;
144  // dumping the arrays
145  array_dump(agent->future, buffer);
146  if(abm_settings.keep_history)
147  array_dump(agent->past, buffer);
148  // sanity check
149  assert(buffer == to_send + agent_dump_size(agent));
150 }
151 
152 
161 unsigned char * abm_do_checkpoint(region_abm_t *region){
162  // calculate dump size
163  size_t chkp_size_tot = region->chkp_size;
164  chkp_size_tot += hash_map_dump_size(region->agents_table);
165  struct _agent_abm_t *agent;
166  unsigned i = hash_map_count(region->agents_table);
167  while(i--){
168  agent = &hash_map_items(region->agents_table)[i];
169  chkp_size_tot += array_dump_size(agent->future);
170  if(abm_settings.keep_history)
171  chkp_size_tot += array_dump_size(agent->past);
172  chkp_size_tot += agent->user_data_size;
173  }
174  // allocate and populate the checkpoint
175  unsigned char *ret = rsalloc(chkp_size_tot), *chk = ret;
176  memcpy(ret, region, region->chkp_size);
177  ret += region->chkp_size;
178  hash_map_dump(region->agents_table, ret);
179  i = hash_map_count(region->agents_table);
180  while(i--){
181  agent = &hash_map_items(region->agents_table)[i];
182  array_dump(agent->future, ret);
183  if(abm_settings.keep_history)
184  array_dump(agent->past, ret);
185  memcpy(ret, agent->user_data, agent->user_data_size);
186  ret += agent->user_data_size;
187  }
188  assert(chk + chkp_size_tot == ret);
189  return chk;
190 }
191 
198 void abm_restore_checkpoint(unsigned char *data, region_abm_t *region){
199  struct _agent_abm_t *agent;
200 
201  assert(((region_abm_t *)data)->chkp_size == region->chkp_size);
202  // free the region allocations
203  unsigned i = hash_map_count(region->agents_table);
204  while(i--){
205  agent = &(hash_map_items(region->agents_table)[i]);
206  array_fini(agent->future);
207  if(abm_settings.keep_history)
208  array_fini(agent->past);
209  rsfree(agent->user_data);
210  }
211  hash_map_fini(region->agents_table);
212  // copy the region back
213  memcpy(region, data, region->chkp_size);
214  data += region->chkp_size;
215  //load the other allocations
216  hash_map_load(region->agents_table, data);
217  i = hash_map_count(region->agents_table);
218  while(i--){
219  agent = &(hash_map_items(region->agents_table)[i]);
220  array_load(agent->future, data);
221  if(abm_settings.keep_history)
222  array_load(agent->past, data);
223  agent->user_data = rsalloc(agent->user_data_size);
224  memcpy(agent->user_data, data, agent->user_data_size);
225  data += agent->user_data_size;
226  }
227 }
228 
229 
235 void abm_layer_init(void) {
236 
237  unsigned actual_neighbours, i, region_alloc_size;
238  unsigned data_offset;
239  region_abm_t *region;
240 
241  // settings_abm is a weak symbol, we check for its existence
242  if(!&abm_settings){
243  return;
244  }
245 
246  const unsigned directions = DirectionsCount();
247  // the ABM API needs an underlying topology for meaningful operations!!!
248  if(!directions){
249  rootsim_error(true, "Topology has not been initialized!");
250  return;
251  }
252 
253  foreach_lp(lp){
254  // we count valid neighbours
255  // todo: for constant topologies we can do better than this, we can just select not crossable regions
256  actual_neighbours = NeighboursCount(lp->gid.to_int);
257 
258  region_alloc_size = (unsigned)(sizeof(region_abm_t) + (sizeof(struct _n_info_t) * directions) + (abm_settings.neighbour_data_size * (actual_neighbours + 1)));
259  // instantiates region struct
260  region = rsalloc(region_alloc_size);
261  memset(region, 0, region_alloc_size);
262  // memory layout is as follows:
263  // BASE REGION STRUCT | ARRAY OF NEIGHBOUR INFOS | PUBLISHED DATA BY SELF | ARRAY OF PUBLISHED DATA BY OTHER REGIONS
264  region->chkp_size = region_alloc_size;
265  // helper offset variable for easier assignment of memory regions
266  data_offset = (unsigned)(sizeof(region_abm_t) + (sizeof(struct _n_info_t) * directions));
267  // the nearest block is used to keep track of published data
268  region->published_data_offset = data_offset;
269  // here we assign a memory region only to valid neighbours
270  for(i = 0; i < directions; ++i){
271  if((region->neighbours_info[i].src_lp = GetReceiver(lp->gid.to_int, i, false)) == DIRECTION_INVALID){
272  region->neighbours_info[i].data_offset = UINT_MAX;
273  }else{
274  data_offset += abm_settings.neighbour_data_size;
275  region->neighbours_info[i].data_offset = data_offset;
276  }
277  }
278  // default
279  region->tracked_data = NULL;
280 
281  // initialize the hash_map for agents
282  hash_map_init(region->agents_table);
283  // initialize mark
284  region->next_mark = lp->gid.to_int;
285  // Save pointer in LP state
286  lp->region = region;
287  }
288 }
289 
290 static void receive_update(void);
291 static void update_neighbours(void);
292 
297 static void on_abm_visit(void){
298  struct _visit_abm_t vis;
299  // parse the entering agent
300  struct _agent_abm_t *agent = agent_from_buffer(current_evt->event_content, current_evt->size);
301 
302  if(array_empty(agent->future) || array_get_at(agent->future, 0).region != current->gid.to_int){
303  // this is an intermediate objective to reach the next destination
304  vis.action = abm_settings.traverse_handler;
305  vis.region = current->gid.to_int;
306  } else {
307  vis = array_remove_at(agent->future, 0);
308  }
309  if(abm_settings.keep_history){
310  vis.time = current_evt->timestamp;
311  // move the visit into the past ones
312  array_push(agent->past, vis);
313  }
314 
315  // seems all ok: user, do whatever you want now (passing current_evt->content works as long as the first
316  // field of the agent struct is the agent key
317  switch_to_application_mode();
318  current->ProcessEvent(current->gid.to_int, current_evt->timestamp, vis.action, current_evt->event_content, sizeof(agent->key), current->current_base_pointer);
319  switch_to_platform_mode();
320 }
321 
326 static void on_abm_leave(void){
327  struct _visit_abm_t vis;
328  unsigned char* to_send;
329  unsigned buffer_size;
330  // we search for the agent who's leaving
331  assert(current_evt->size == sizeof(struct _leave_evt));
332  struct _agent_abm_t *agent = hash_map_lookup(current->region->agents_table, ((struct _leave_evt *)current_evt->event_content)->key);
333  if(!agent || agent->leave_time > current_evt->timestamp) {
334  // the exiting agent has been killed or already left or the agent is trying to leave too early (can happen if user decides so)
335  return; // since this is spurious we can directly return
336  }
337 
338  // seems all ok: user, do whatever you want now
339  switch_to_application_mode();
340  current->ProcessEvent(current->gid.to_int, current_evt->timestamp, ((struct _leave_evt *)current_evt->event_content)->leave_code, current_evt->event_content, sizeof(agent->key), current->current_base_pointer);
341  switch_to_platform_mode();
342  // we search again for the agent who's leaving (the user could have possibly killed him)
343  agent = hash_map_lookup(current->region->agents_table, ((struct _leave_evt *)current_evt->event_content)->key);
344  if(!agent || agent->leave_time > current_evt->timestamp){
345  // the capricious user has decided against the agent departure
346  return;
347  }
348  // well, the agent is actually leaving after all...
349  unsigned next_hop = UINT_MAX;
350  switch(topology_settings.type){
351  case TOPOLOGY_OBSTACLES:
352  // fixme: the user may want to use binary topologies with wandering agents
353  case TOPOLOGY_COSTS:
354  // if we have no set destination no point in selecting a next hop
355  if(!array_empty(agent->future)){
356  if(array_peek(agent->future).region == current->gid.to_int){
357  // we don't need to move
358  next_hop = current->gid.to_int;
359  }else{
360  // we get one step closer to the next destination
361  next_hop = FindReceiverToward(array_peek(agent->future).region);
362  }
363  }
364  break;
366  // in topology probabilities the agents just wander
367  next_hop = FindReceiver();
368  break;
369  default:
370  rootsim_error(true, "unsupported");
371  }
372 
373  if(next_hop == UINT_MAX){
374  // TODO communicate the user about the failed leave, maybe call user's ProcessEvent()
375  // with a proper event type, this can happen legitimately (for example the agents is
376  // surrounded by obstacles regions)
377  return;
378  }
379 
380  if(current->gid.to_int == next_hop) {
381  // if the next chosen destination is the very same region we are already in
382  // we simply call ProcessEvent() again
383  if(array_empty(agent->future) || array_peek(agent->future).region != current->gid.to_int){
384  vis.region = current->gid.to_int;
385  vis.action = abm_settings.traverse_handler;
386  }else{
387  // we remove the current visit
388  vis = array_remove_at(agent->future, 0);
389  }
390  if(abm_settings.keep_history){
391  // set the visit time
392  vis.time = current_evt->timestamp;
393  // we move the visit into the past ones
394  array_push(agent->past, vis);
395  }
396  // seems all ok: user, do whatever you want now
397  switch_to_application_mode();
398  current->ProcessEvent(current->gid.to_int, current_evt->timestamp, vis.action, current_evt->event_content, sizeof(agent->key), current->current_base_pointer);
399  switch_to_platform_mode();
400  } else {
401  buffer_size = agent_dump_size(agent);
402 
403  to_send = rsalloc(buffer_size);
404 
405  agent_to_buffer(agent, to_send);
406  // finally we schedule the agent
407  UncheckedScheduleNewEvent(next_hop, current_evt->timestamp, ABM_VISITING, to_send, buffer_size);
408  // now we can get rid of it
409  KillAgent(agent->key);
410  // remember to free that stuff if we mallocated it!
411  rsfree(to_send);
412  }
413 }
414 
419 static void receive_update(void){
420  //if(abm_settings.neighbour_data_size != current_evt->size)
421  //rootsim_error(true, "Misuse of ABM api, regions do not agree on neighbours info size! EXITING!");
422 
423  region_abm_t *region = current->region;
424  unsigned i = DirectionsCount();
425  while(i--){// FIXME linear search, maybe sort them at init for log search or sort by direction for constant indexing
426  if(region->neighbours_info[i].src_lp == current_evt->sender.to_int){
427  memcpy(((unsigned char *)region) + region->neighbours_info[i].data_offset, current_evt->event_content, abm_settings.neighbour_data_size);
428  return;
429  }
430  }
431  rootsim_error(true, "Misuse of ABM api, unable to find neighbours info's memory area! EXITING!");
432 }
433 
438 static void update_neighbours(void){
439  region_abm_t *region = current->region;
440  unsigned char* published_data = ((unsigned char *)region) + region->published_data_offset;
441  // we check whether we need to update our neighbours about some changes in the tracked data
442  if(!region->tracked_data || !memcmp(published_data, region->tracked_data, abm_settings.neighbour_data_size))
443  return;
444 
445  // copy the new data into the tracked one
446  memcpy(published_data, region->tracked_data, abm_settings.neighbour_data_size);
447 
448  // let's propagate the changes to other regions too
449  unsigned i = DirectionsCount();
450  while(i--){
451  if(region->neighbours_info[i].src_lp != DIRECTION_INVALID){
452  UncheckedScheduleNewEvent(region->neighbours_info[i].src_lp, current_evt->timestamp, ABM_UPDATE, published_data, abm_settings.neighbour_data_size);
453  }
454  }
455 }
456 
461 void ProcessEventABM(void) {
462  switch (current_evt->type) {
463 
464  case ABM_VISITING:
465  on_abm_visit();
466  break;
467 
468  case ABM_LEAVING:
469  on_abm_leave();
470  break;
471 
472  case ABM_UPDATE:
473  receive_update();
474  // we didn't give control to the user, no need to check changes in the
475  // neighbours infos memory.
476  return;
477  default:
478  // uninteresting stuff, do as we don't exist
479  ProcessEventTopology();
480  break;
481 
482  }
484 }
485 
486 int GetNeighbourInfo(direction_t i, unsigned int *region_id, void **data_p){
487  region_abm_t *region = current->region;
488  if(unlikely(i >= DirectionsCount()))
489  rootsim_error(true, "bad argument in abm call");
490 
491  if(region->neighbours_info[i].src_lp == DIRECTION_INVALID)
492  return -1;
493 
494  *region_id = region->neighbours_info[i].src_lp;
495  *data_p = ((unsigned char *)region) + region->neighbours_info[i].data_offset;
496  return 0;
497 }
498 
499 void TrackNeighbourInfo(void *neighbour_data) {
500  current->region->tracked_data = neighbour_data;
501 }
502 
503 bool IterAgents(agent_t *agent_p) {
504  switch_to_platform_mode();
505  // fixme preemption breaks this
506  static __thread map_size_t closure = 0;
507  if(!agent_p || closure >= hash_map_count(current->region->agents_table)) {
508  closure = 0;
509  return false;
510  }
511  *agent_p = hash_map_items(current->region->agents_table)[closure++].key;
512  switch_to_application_mode();
513  return true;
514 }
515 
516 unsigned CountAgents(void) {
517  return hash_map_count(current->region->agents_table);
518 }
519 
520 agent_t SpawnAgent(unsigned user_data_size) {
521  switch_to_platform_mode();
522 
523  unsigned long long new_key = get_agent_mark(current->region);
524  // new agent
525  struct _agent_abm_t *ret = hash_map_reserve_elem(current->region->agents_table, new_key);
526 
527  array_init(ret->future);
528 
530  ret->user_data = rsalloc(user_data_size);
531  ret->key = new_key;
532 
533  // we register the visit to THIS region
534  if(abm_settings.keep_history){
535  array_init(ret->past);
536  struct _visit_abm_t start_visit = { current->gid.to_int, ACTION_START, current_evt->timestamp };
537  array_push(ret->past, start_visit);
538  }else{
539  memset(&ret->past, 0, sizeof(ret->past));
540  }
541  switch_to_application_mode();
542  return ret->key;
543 }
544 
545 void KillAgent(agent_t agent_id) {
546  switch_to_platform_mode();
547  struct _agent_abm_t *agent = retrieve_agent(agent_id);
548 
549  array_fini(agent->future);
550  if(abm_settings.keep_history)
551  array_fini(agent->past);
552  rsfree(agent->user_data);
553 
554  hash_map_delete_elem(current->region->agents_table, agent);
555  switch_to_application_mode();
556 }
557 
558 void * DataAgent(agent_t agent_id, unsigned *data_size_p){
559  struct _agent_abm_t *agent = retrieve_agent(agent_id);
560  if(data_size_p)
561  *data_size_p = agent->user_data_size;
562  return agent->user_data;
563 }
564 
565 void ScheduleNewLeaveEvent(simtime_t time, unsigned int event_type, agent_t agent_id) {
566  switch_to_platform_mode();
567 
568  struct _agent_abm_t *agent = retrieve_agent(agent_id);
569  // we mark the agent with the intended leave time so we can later compare it
570  // to check for spurious events
571  agent->leave_time = time;
572 
573  struct _leave_evt leave_evt = {agent_id, event_type};
574 
575  UncheckedScheduleNewEvent(current->gid.to_int, agent->leave_time, ABM_LEAVING, &leave_evt, sizeof(leave_evt));
576  //array_push(current->region->agents_leaving, agent->key);
577 
578  switch_to_application_mode();
579 }
580 
581 unsigned CountPastVisits(agent_t agent_id){
582  return array_count(retrieve_agent(agent_id)->past);
583 }
584 
585 void GetPastVisit(agent_t agent_id, unsigned *region_p, unsigned *event_type_p, simtime_t *time_p, unsigned i){
586  switch_to_platform_mode();
587 
588  struct _agent_abm_t *agent = retrieve_agent(agent_id);
589  if(unlikely(i >= array_count(agent->past)))
590  rootsim_error(true, "trying to access an out of bounds past visit from agent %llu", agent_id);
591  // we count backward to map the nearest event in the past to index 0
592  *region_p = array_get_at(agent->past, array_count(agent->past) - 1 - i).region;
593  *event_type_p = array_get_at(agent->past, array_count(agent->past) - 1 - i).action;
594  *time_p = array_get_at(agent->past, array_count(agent->past) - 1 - i).time;
595 
596  switch_to_application_mode();
597 }
598 
599 unsigned CountVisits(agent_t agent_id){
600  return array_count(retrieve_agent(agent_id)->future);
601 }
602 
603 void GetVisit(agent_t agent_id, unsigned *region_p, unsigned *event_type_p, unsigned i){
604  struct _agent_abm_t *agent = retrieve_agent(agent_id);
605  if(unlikely(i >= array_count(agent->future)))
606  rootsim_error(true, "trying to access an out of bounds future visit from agent %llu", agent_id);
607 
608  *region_p = array_get_at(agent->future, i).region;
609  *event_type_p = array_get_at(agent->future, i).action;
610 }
611 
612 void SetVisit(agent_t agent_id, unsigned region, unsigned event_type, unsigned i){
613  struct _agent_abm_t *agent = retrieve_agent(agent_id);
614  if(unlikely(i >= array_count(agent->future) || event_type == ACTION_START || region >= RegionsCount())) {
615  rootsim_error(true, "bad argument in abm call for agent %llu", agent_id);
616  }
617 
618  array_items(agent->future)[i].region = region;
619  array_items(agent->future)[i].action = event_type;
620 }
621 
622 void EnqueueVisit(agent_t agent_id, unsigned region, unsigned event_type){
623  struct _agent_abm_t *agent = retrieve_agent(agent_id);
624  if(unlikely(event_type == ACTION_START || region >= RegionsCount())) {
625  rootsim_error(true, "bad argument in abm call for agent %llu", agent_id);
626  }
627 
628  struct _visit_abm_t visit = {region, event_type, INFINITY};
629  array_push(agent->future, visit);
630 }
631 
632 void AddVisit(agent_t agent_id, unsigned region, unsigned event_type, unsigned i){
633  struct _agent_abm_t *agent = retrieve_agent(agent_id);
634  if(unlikely(i > array_count(agent->future) || event_type == ACTION_START || region >= RegionsCount())) {
635  rootsim_error(true, "bad argument in abm call for agent %llu", agent_id);
636  }
637 
638  struct _visit_abm_t visit = {region, event_type, INFINITY};
639 
640  if(i == array_count(agent->future))
641  array_push(agent->future, visit);
642  else
643  array_add_at(agent->future, i, visit);
644 }
645 
646 void RemoveVisit(agent_t agent_id, unsigned i) {
647  struct _agent_abm_t *agent = retrieve_agent(agent_id);
648  if(i >= array_count(agent->future)) {
649  rootsim_error(true, "bad argument in abm call for agent %llu", agent_id);
650  }
651 
652  array_remove_at(agent->future, i);
653 }
all crossing costs and probabilities are set to 1, but there can be not crossable regions ...
Definition: ROOT-Sim.h:126
void abm_layer_init(void)
Definition: abm_layer.c:235
Initialization routines.
void ProcessEventABM(void)
Definition: abm_layer.c:461
A generic invalid direction.
Definition: ROOT-Sim.h:122
ROOT-Sim header for model development.
unsigned char * abm_do_checkpoint(region_abm_t *region)
Definition: abm_layer.c:161
unsigned int to_int
The GID numerical value.
Definition: core.h:133
unsigned user_data_size
UUID that uniquely identifies the agent (this must be the first field of the struct for various reaso...
Definition: abm_layer.c:54
The ROOT-Sim scheduler main module header.
This is the implementation of a dynamic array, used for managing various data structures.
__thread struct lp_struct * current
This is a per-thread variable pointing to the block state of the LP currently scheduled.
Definition: scheduler.c:72
double simtime_t
This defines the type with whom timestamps are represented.
Definition: ROOT-Sim.h:55
__thread msg_t * current_evt
Definition: scheduler.c:83
decisions are taken at random but are weighted on the palatability of neighbours
Definition: ROOT-Sim.h:128
unsigned int size
Unique identifier of the message, used for rendez-vous events.
Definition: core.h:189
enum _direction_t direction_t
static void receive_update(void)
Definition: abm_layer.c:419
static void update_neighbours(void)
Definition: abm_layer.c:438
This header implements a simple hash map data structure.
region_abm_t * region
pointer to the region struct
Definition: process.h:155
GID_t gid
Global ID of the LP.
Definition: process.h:82
static struct _agent_abm_t * agent_from_buffer(const unsigned char *event_content, unsigned event_size)
Definition: abm_layer.c:103
static void agent_to_buffer(struct _agent_abm_t *agent, unsigned char *buffer)
Definition: abm_layer.c:136
decisions on next hops are taken based on the costs undertaken to cross the boundaries ...
Definition: ROOT-Sim.h:127
void(* ProcessEvent)(unsigned int me, simtime_t now, int event_type, void *event_content, unsigned int size, void *state)
Definition: process.h:136
char * user_data
This must be the seonc filed of this struct for serialization reasons.
Definition: abm_layer.c:55
static unsigned agent_dump_size(const struct _agent_abm_t *agent)
Definition: abm_layer.c:90
void abm_restore_checkpoint(unsigned char *data, region_abm_t *region)
Definition: abm_layer.c:198
static void on_abm_visit(void)
Definition: abm_layer.c:297
void * current_base_pointer
The current state base pointer (updated by SetState())
Definition: process.h:100
static void on_abm_leave(void)
Definition: abm_layer.c:326
#define unlikely(exp)
Optimize the branch as likely not taken.
Definition: core.h:74