rambrain
cyclicManagedMemory.cpp
Go to the documentation of this file.
1 /* rambrain - a dynamical physical memory extender
2  * Copyright (C) 2015 M. Imgrund, A. Arth
3  * mimgrund (at) mpifr-bonn.mpg.de
4  * arth (at) usm.uni-muenchen.de
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "cyclicManagedMemory.h"
21 #include "common.h"
22 #include "exceptions.h"
23 #include "rambrain_atomics.h"
24 #include "managedSwap.h"
25 #include <pthread.h>
26 #include <cmath>
27 //#define VERYVERBOSE
28 
29 
30 
31 
32 
33 
34 
35 namespace rambrain
36 {
37 #ifdef VERYVERBOSE
39 
40 #define VERBOSEPRINT(x) printf("\n<--%s\n",x); if (memChunks.size()>=min_elements) printCycle();printf("\n%s---->\n",x);
41 #else
42 #define VERBOSEPRINT(x) ;
43 #endif
44 
45 pthread_mutex_t cyclicManagedMemory::cyclicTopoLock = PTHREAD_MUTEX_INITIALIZER;
46 
48 {
49 }
50 
51 
53 {
54  BACKLOG_ADD_ID ( REGISTER, chunk.id )
55  cyclicAtime *neu = new cyclicAtime;
56 
57  //Couple chunk to atime and vice versa:
58  neu->chunk = &chunk;
59  chunk.schedBuf = ( void * ) neu;
60  rambrain_pthread_mutex_lock ( &cyclicTopoLock );
61  if ( active == NULL ) { // We're inserting first one
62  neu->prev = neu->next = neu;
63  counterActive = neu;
64  } else { //We're inserting somewhen.
65  VERBOSEPRINT ( "registerbegin" );
66  cyclicAtime *before = active->prev;
67  cyclicAtime *after = active;
68  MUTUAL_CONNECT ( before, neu );
69  MUTUAL_CONNECT ( neu, after );
70  }
72  counterActive = neu;
73  }
74  active = neu;
75  rambrain_pthread_mutex_unlock ( &cyclicTopoLock );
76 }
77 
78 
79 
81 {
82  BACKLOG_ADD_ID ( DELETE, chunk.id )
83  cyclicAtime *element = ( cyclicAtime * ) chunk.schedBuf;
84  //Memory counting for what we account for:
85  rambrain_pthread_mutex_lock ( &cyclicTopoLock );
86  if ( chunk.status == MEM_SWAPPED || chunk.status == MEM_SWAPOUT ) {
87 
88  } else if ( chunk.preemptiveLoaded ) {
89  preemptiveBytes -= chunk.size;
90  chunk.preemptiveLoaded = false;
91  }
92 
93 
94  if ( preemptiveStart && &chunk == preemptiveStart->chunk ) {
96  }
97 
98  //Hook out element:
99  if ( element->next == element ) {
100  active = NULL;
101  counterActive = NULL;
102  delete element;
103  rambrain_pthread_mutex_unlock ( &cyclicTopoLock );
104  return;
105 
106  }
107 
108  if ( element->next == element->prev ) {
109  cyclicAtime *remaining = element->next;
110  remaining->next = remaining->prev = remaining;
111  } else {
112  element->next->prev = element->prev;
113  element->prev->next = element->next;
114  }
115  if ( active == element ) {
116  active = element->next;
117  }
118 
119  if ( counterActive == element ) {
120  counterActive = element->prev;
121  }
122 
123  delete element;
124  rambrain_pthread_mutex_unlock ( &cyclicTopoLock );
125 }
126 
127 //Touch happens automatically after use, create, swapIn
129 {
130  rambrain_pthread_mutex_lock ( &cyclicTopoLock );
131  BACKLOG_ADD_ID ( TOUCH, chunk.id )
132  if ( chunk.preemptiveLoaded ) { //This chunk was preemptively loaded
134  preemptiveBytes -= chunk.size;
135 
136  chunk.preemptiveLoaded = false;
137  } else {
139  }
140  // This can be the case even if chunk.preemptiveLoaded is false when we have just swapped in this one as active
141  if ( preemptiveStart && ( preemptiveStart->chunk == &chunk ) ) {
142  if ( preemptiveStart->next == active ) {
143  preemptiveStart = NULL;
144  } else {
146  }
147  }
148 
149  //Put this object to begin of loop:
150  cyclicAtime *element = ( cyclicAtime * ) chunk.schedBuf;
151 
152  if ( active == element ) {
153  rambrain_pthread_mutex_unlock ( &cyclicTopoLock );
154  return true;
155  }
156 
157  if ( counterActive == element ) {
159  }
160 
161  if ( active->prev == element ) {
162  active = element;
163  rambrain_pthread_mutex_unlock ( &cyclicTopoLock );
164  return true;
165  };
166 
167 
168 
169  if ( active->next == element ) {
170  cyclicAtime *before = active->prev;
171  cyclicAtime *after = element->next;
172  MUTUAL_CONNECT ( before, element );
173  MUTUAL_CONNECT ( element, active );
174  MUTUAL_CONNECT ( active, after );
175  active = element;
176  rambrain_pthread_mutex_unlock ( &cyclicTopoLock );
177  return true;
178  }
179 
180  cyclicAtime *oldplace_before = element->prev;
181  cyclicAtime *oldplace_after = element->next;
182  cyclicAtime *before = active->prev;
183  cyclicAtime *after = active;
184 
185  MUTUAL_CONNECT ( oldplace_before, oldplace_after );
186  MUTUAL_CONNECT ( before, element );
187  MUTUAL_CONNECT ( element, after );
188  active = element;
189  rambrain_pthread_mutex_unlock ( &cyclicTopoLock );
190  return true;
191 }
192 
194 {
195  bool old = preemtiveSwapIn;
196  preemtiveSwapIn = preemptive;
197  return old;
198 }
199 
201 {
202  bool old = preemtiveSwapOut;
203  preemtiveSwapOut = preemptive;
204  return old;
205 }
206 
208 {
209  global_bytesize claimed_use = swap->getUsedSwap();
210  fprintf ( stderr, "%lu\t%lu=%lu\t%lu\n", memory_used, claimed_use, memory_swapped, preemptiveBytes );
211 }
213 {
214  if ( !separated.from ) {
215  return;
216  }
217  cyclicAtime *before = pos->prev;
218  cyclicAtime *after = pos;
219  MUTUAL_CONNECT ( before, separated.from );
220  MUTUAL_CONNECT ( separated.to, after );
221 }
222 
223 
224 cyclicManagedMemory::chain cyclicManagedMemory::filterChain ( chain &toFilter, const memoryStatus *separateStatus, bool *preemptiveLoaded )
225 {
226  cyclicAtime *cur = toFilter.from;
227  cyclicAtime *sepaStart = NULL;
228  struct cyclicManagedMemory::chain separated = {NULL, NULL};
229 
230  //First, insert an element at end to cope with all types (cyclic, noncyclic) at once:
231  cyclicAtime end;
232  cyclicAtime *after = toFilter.to->next;
233  cyclicAtime *before = toFilter.from->prev;
234  bool cyclic = ( after == toFilter.from );
235  cyclicAtime *firstValid = NULL, *lastValid = NULL;
236  MUTUAL_CONNECT ( toFilter.to, ( &end ) );
237  MUTUAL_CONNECT ( ( &end ), toFilter.from );
238 
239  do {
240  //determine whether to separate this chunk:
241  bool separateThis = false;
242  for ( const memoryStatus *status = separateStatus; ( *status != MEM_ROOT ); ++status ) {
243  if ( cur->chunk->status == *status ) {
244  separateThis = true;
245  goto afterchecks;
246  }
247  }
248  if ( preemptiveLoaded ) {
249  separateThis = ! ( cur->chunk->preemptiveLoaded ^ *preemptiveLoaded );
250  }
251 afterchecks:
252  if ( separateThis ) { // we should separate this element
253  if ( !sepaStart ) { // if its the first to separate, lets mark this as start
254  sepaStart = cur;
255  }
256 
257  } else { // We should not separate this
258  if ( !firstValid ) {
259  firstValid = cur;
260  }
261  lastValid = cur;
262  if ( sepaStart ) { //We had started separating something, so lets cut these out
263  cyclicAtime *newTo = cur->prev;
264  MUTUAL_CONNECT ( sepaStart->prev, cur );
265  if ( separated.from == NULL ) { // first separated element
266  separated.from = sepaStart;
267 
268  } else { //We had previously separated elements, lets chain the end of the formerly separated to this one:
269  MUTUAL_CONNECT ( separated.to, sepaStart );
270  }
271  separated.to = newTo;
272 
273  sepaStart = NULL;
274  } else {
275  //We are not in a separate region and do not have a chain to end, so nothing happens.
276  }
277  }
278  cur = cur->next;
279  } while ( cur != &end );
280 
281  //The cur==to element is by definition out of selection, thus we may close the separated area if it exists:
282  if ( sepaStart ) { //We had started separating something, so lets cut these out
283  cyclicAtime *newTo = cur->prev;
284  MUTUAL_CONNECT ( sepaStart->prev, cur );
285  if ( separated.from == NULL ) { // first separated element
286  separated.from = sepaStart;
287 
288  } else { //We had previously separated elements, lets chain the end of the formerly separated to this one:
289  MUTUAL_CONNECT ( separated.to, sepaStart );
290  }
291  separated.to = newTo;
292  }
293  toFilter = {firstValid, lastValid};
294  if ( lastValid ) {
295  if ( !cyclic ) {
296  MUTUAL_CONNECT ( before, firstValid );
297  MUTUAL_CONNECT ( lastValid, after );
298  } else {
299  MUTUAL_CONNECT ( lastValid, firstValid );
300  }
301  } else {
302  if ( !cyclic ) {
303  MUTUAL_CONNECT ( before, after );
304  }
305  }
306  return separated;
307 }
308 
310 {
311  BACKLOG_ADD_SIZE ( DECAY, bytes )
312  if ( preemptiveStart == NULL ) {
313  return;
314  }
315  global_bytesize swapleft = swap->getFreeSwap();
316  bytes = min ( preemptiveBytes, bytes );
317  bytes = min ( swapleft, bytes );
318  swapleft = min ( preemptiveBytes, swapleft ); //This makes the hard limit clear.
320  global_bytesize bytesselected = 0;
321  unsigned int chunks = 0;
322  bool consecutive = true;
323  while ( cur != active && bytesselected < bytes ) {
324  if ( cur->chunk->size + bytesselected < swapleft && cur->chunk->status == MEM_ALLOCATED && cur->chunk->useCnt == 0 ) {
325  bytesselected += cur->chunk->size;
326  ++chunks;
327  cur->chunk->preemptiveLoaded = false;
328  } else {
329  consecutive = false;
330  }
331  cur = cur->next;
332  }
333  if ( cur == preemptiveStart ) {
334  return;
335  }
337  managedMemoryChunk *chunklist[chunks];
338  managedMemoryChunk **cursw = chunklist;
339  bytesselected = 0;
340  while ( cur2 != cur && bytesselected < bytes ) {
341  if ( cur2->chunk->size + bytesselected < swapleft && cur2->chunk->status == MEM_ALLOCATED && cur2->chunk->useCnt == 0 ) {
342  *cursw = cur2->chunk;
343  ++cursw;
344  bytesselected += cur2->chunk->size;
345  }
346  cur2 = cur2->next;
347  }
348  if ( swap->swapOut ( chunklist, chunks ) != bytesselected ) {
349  Throw ( rambrain::memoryException ( "Could not swap out bytes even though swap claimed to be able to swap out this." ) );
350  }
351  preemptiveBytes -= bytesselected;
352 #ifdef SWAPSTATS
353  ++n_swap_out;
354  swap_out_scheduled_bytes += bytesselected;
355 #endif
356 
357  rambrain_pthread_mutex_lock ( &cyclicTopoLock );
358 
359  if ( !consecutive ) {
360  //Take out all chunks that we have swapped out:
363  const memoryStatus filterMe[] = {MEM_SWAPOUT, MEM_SWAPPED, MEM_ROOT}; //Mem_root terminates list.
364  chain area = {from, cur};
365  struct chain separated = filterChain ( area, filterMe ); // After this action, from->prev->next to cur2 only holds preemtive stuff.
366  preemptiveStart = preemptiveStart->next;//Lets move again into area that only holds preemptives now.
367  insertBefore ( preemptiveStart, separated );
369 
370  } else {
371  preemptiveStart = ( cur == active ? NULL : cur );
372 
373  }
374 
375 
376  rambrain_pthread_mutex_unlock ( &cyclicTopoLock );
377 
378 }
379 
380 
382 {
383  BACKLOG_ADD_ID ( SWAPIN, chunk.id )
384  VERBOSEPRINT ( "swapInEntry" );
385 #ifdef VERYVERBOSE
386  if ( chunk.useCnt == 0 ) {
387  printf ( "Unprotected!!!\n;" );
388  }
389  printf ( "Main Subject %lu\n", chunk.id );
390 #endif
391  if ( chunk.status & MEM_ALLOCATED || chunk.status == MEM_SWAPIN ) {
392  return true;
393  }
394 
395  global_bytesize max_preemptive = ( swapInFrac - swapOutFrac ) * memory_max;
396  double prob_random_preempt = pow ( swapInFrac - swapOutFrac, consecutivePreemptiveTransactions );
397 
398  if ( 0.01 > prob_random_preempt || pow ( swapInFrac - swapOutFrac, preemptiveSinceLast ) > .01 ) {
399  global_bytesize preemptiveReduction = 2.* ( max_preemptive - preemptiveBytes ) + 1;
400  decay ( preemptiveReduction );
401  }
403 
404  // We use the old border to ensure that sth is not swapped in again that was just swapped out.
405  cyclicAtime *oldBorder = counterActive;
406 
407 
408  global_bytesize actual_obj_size = chunk.size;
409  //We want to read in what the user requested plus fill up the preemptive area with opportune guesses
410 
411  bool preemptiveAutoon = ( ( double ) swap->getFreeSwap() ) / swap->getSwapSize() > preemptiveTurnoffFraction;
412 
413  if ( preemtiveSwapIn && preemptiveAutoon ) {
414 #ifdef VERYVERBOSE
415  if ( preemptiveBytes > max_preemptive ) {
416  warnmsg ( "Loaded more premptively than suggested!" );
417  }
418 #endif
419 
420  global_bytesize targetReadinVol = actual_obj_size + ( swapInFrac - swapOutFrac ) * memory_max - preemptiveBytes;
421 #ifdef VERYVERBOSE
422  printf ( "Preemptive swapin (premptiveBytes = %lu) (targetReadinVol = %lu)\n", preemptiveBytes, targetReadinVol );
423 #endif
424 
425  //Swap out if we want to read in more than what we thought:
426 
427  if ( targetReadinVol + memory_used > memory_max ) {
428 #ifdef VERYVERBOSE
429  printf ( "We do not have space to get fully preemptive, lets try swap something out\n" );
430 #endif
431  global_bytesize targetSwapoutVol = actual_obj_size + ( 1. - swapOutFrac ) * memory_max - preemptiveBytes;
432  targetReadinVol = targetSwapoutVol;
433  swapErrorCode err = swapOut ( targetReadinVol ); // A simple call to ensureEnoughSpace is not enough, we want to control what happens on error.
434  if ( err != ERR_SUCCESS ) {
435 #ifdef VERYVERBOSE
436  printf ( "We could not swap out enough, lets retry with the object only (which is the minimum)\n" );
437 #endif
438  //We could not swap out enough, lets retry with the object only (which is the minimum)
439  bool alreadyThere = ensureEnoughSpace ( actual_obj_size, &chunk );
440  if ( alreadyThere ) {
441  waitForSwapin ( chunk, true );
442  VERBOSEPRINT ( "Is Already swapped in by so else" );
443  return true;
444  }
445  targetReadinVol = actual_obj_size;
446  }
447  if ( ensureEnoughSpace ( targetReadinVol, &chunk ) ) {
448  waitForSwapin ( chunk, true );
449  VERBOSEPRINT ( "Is Already swapped in by so else" );
450  return true;
451  }
452  }
453 #ifdef VERYVERBOSE
454  else {
455  printf ( "We got enough space right away.\n" );
456  }
457 #endif
458  VERBOSEPRINT ( "swapInAfterSwap" );
459  cyclicAtime *readEl = ( cyclicAtime * ) chunk.schedBuf;
460  cyclicAtime *cur = readEl;
461  cyclicAtime *endSwapin = readEl;
462  global_bytesize selectedReadinVol = 0;
463  global_bytesize preemtivelySelected = 0;
464  unsigned int numberSelected = 0;
465  rambrain_pthread_mutex_lock ( &cyclicTopoLock );
466 
467 #ifdef VERYVERBOSE
468  printf ( "Starting swapin selection" );
469 #endif
470 
471  max_preemptive -= preemptiveBytes; //Our limit for this transaction.
472 
473  //Why do we not have to check for chunk's status?
474  // Because, as we should load in a swapped element, we're in the swapped section,
475  // which only contains swapped elements until counterActive is reached.
476  do {
477 #ifdef VERYVERBOSE
478  printf ( "Chunk %lu has %lu bytes, this is %ld over the top\n", cur->chunk->id, cur->chunk->size, selectedReadinVol + cur->chunk->size + memory_used - memory_max );
479 #endif
480  if ( selectedReadinVol + cur->chunk->size + memory_used <= memory_max && cur->chunk->status == MEM_SWAPPED && ( selectedReadinVol == 0 || ( preemtivelySelected + cur->chunk->size <= max_preemptive ) ) ) {
481 
482  cur->chunk->preemptiveLoaded = ( selectedReadinVol > 0 ? true : false );
483  ++numberSelected;
484 
485 
486  if ( selectedReadinVol > 0 ) {
487  preemtivelySelected += cur->chunk->size;
488  }
489  selectedReadinVol += cur->chunk->size;
490  if ( selectedReadinVol >= targetReadinVol || ( selectedReadinVol > 0 && preemtivelySelected == max_preemptive ) ) {
491  break;
492  }
493 #ifdef VERYVERBOSE
494  printf ( "swapin %d\n", cur->chunk->id );
495 #endif
496  }
497  cur = cur->prev;
498  } while ( cur != oldBorder );
499 
500  managedMemoryChunk *chunks[numberSelected];
501  unsigned int n = 0;
502  global_bytesize selectedReadinVol2 = 0;
503  preemtivelySelected = 0;
504  bool activeInList = false;
505 
506  do {
507  if ( readEl == active ) {
508  activeInList = true;
509  }
510  if ( selectedReadinVol2 + readEl->chunk->size + memory_used <= memory_max && readEl->chunk->status == MEM_SWAPPED && ( selectedReadinVol2 == 0 || ( preemtivelySelected + readEl->chunk->size <= max_preemptive ) ) ) {
511  chunks[n++] = readEl->chunk;
512 
513  if ( selectedReadinVol2 > 0 ) {
514  preemtivelySelected += readEl->chunk->size;
515  }
516  selectedReadinVol2 += readEl->chunk->size;
517  if ( selectedReadinVol2 >= targetReadinVol || ( selectedReadinVol2 > 0 && preemtivelySelected == max_preemptive ) ) {
518  break;
519  }
520  }
521  readEl = readEl->prev;
522  } while ( readEl != oldBorder );
523  preemptiveSinceLast = numberSelected - 1;
524 
525  global_bytesize swappedInBytes = swap->swapIn ( chunks, numberSelected );
526  if ( ( swappedInBytes != selectedReadinVol ) ) {
527  //Check if we at least have swapped in enough:
528  VERBOSEPRINT ( "exiting with non complete job" );
529  if ( ! ( chunk.status & MEM_ALLOCATED || chunk.status == MEM_SWAPIN ) ) {
530  return Throw ( memoryException ( "managedSwap failed to swap in :-(" ) );
531  }
532 
533  }
534 #ifdef VERYVERBOSE
535  printf ( "endSwapin is at %lu", chunks[numberSelected - 1]->id );
536 #endif
537 
538 
539  VERBOSEPRINT ( "Before reordering" );
540  preemptiveBytes += selectedReadinVol - actual_obj_size;
541 
542  if ( readEl == oldBorder ) { // Correct for boundary too long when hitting counterActive.
543  readEl = readEl->next;
544  }
545 
546  //one difficulty here is that active ptr marks our insertion point. However, it is not easy to track where active
547  //actually is sitting in, be it the filtered section, the 'good' section or outside the filtered area. Normally,
548  //it is just outside, however in rare circumstances, active is inside. We need to keep track of these cases without
549  //producing much overhead. The following seems to work now and we hope it captures all circumstances.
550 
551  if ( activeInList ) { // Correct when active was also moved in action. This will exclude cyclic things downstairs, as we have an element out of here, endSwapin.
552  if ( active == endSwapin ) {
553  active = active->next;
554  }
555  }
556 
557  cyclicAtime *after = endSwapin->next;
558  if ( after == readEl ) {
559  after = NULL; //mark if were cyclic.
560  }
561 
562  chain toFilter = {readEl, endSwapin};
563 
565  chain filtered = filterChain ( toFilter, justSwappedin );
566  if ( !preemptiveStart ) {
567  preemptiveStart = filtered.from;
569  preemptiveStart = NULL;
570  }
571  }
572  //Let us now reinsert:
573  if ( after ) { // We are not cyclic
574  if ( active ) {
575  insertBefore ( active, filtered );
576  } else {
577  active = after;
578  insertBefore ( after, filtered );
579  }
580  } else { // We are cyclic
581  counterActive = readEl;
582  if ( toFilter.from ) {
583  insertBefore ( toFilter.from, filtered );
584  } else {
585  MUTUAL_CONNECT ( filtered.to, filtered.from )
586  }
587  }
588  rambrain_pthread_mutex_unlock ( &cyclicTopoLock );
589  touch ( chunk );
590  if ( counterActive->chunk->status == MEM_SWAPPED ) {
592  }
593 
594 #ifdef SWAPSTATS
595  swap_in_scheduled_bytes += swappedInBytes;
596  n_swap_in += 1;
597 #endif
598  VERBOSEPRINT ( "swapInBeforeReturn" );
599  return true;
600 
601  } else {
602 #ifdef VERYVERBOSE
603  printf ( "Non-preemptive swapin\n" );
604 #endif
605  bool alreadyThere = ensureEnoughSpace ( actual_obj_size, &chunk );
606  if ( alreadyThere ) {
607  return true;
608  }
609 
610 
611  //We have to check wether the block is still swapped or not
612  if ( swap->swapIn ( &chunk ) == chunk.size ) {
613  //Wait for object to be swapped in:
614  touch ( chunk );
615  rambrain_pthread_mutex_lock ( &cyclicTopoLock );
616  if ( counterActive->chunk->status == MEM_SWAPPED ) {
618  }
619 
620 #ifdef SWAPSTATS
621  swap_in_scheduled_bytes += chunk.size;
622  n_swap_in += 1;
623 #endif
624  rambrain_pthread_mutex_unlock ( &cyclicTopoLock );
625  return true;
626  } else {
627  //Unlock mutex under which we were called, as we'll be throwing...
628  rambrain_pthread_mutex_unlock ( &stateChangeMutex );
629  return Throw ( memoryException ( "Could not swap in an element." ) );
630  };
631  }
632 
633 }
634 
635 
637 {
638  if ( !preemtiveSwapOut ) {
639  return;
640  }
641  global_bytesize keep_free_for_user = ( 1. - swapInFrac ) * ( memory_max );
642  global_bytesize total_preemptive_needed = preemtiveSwapIn ? ( swapInFrac - swapOutFrac ) * ( memory_max ) - preemptiveBytes : 0;
643  //We also account for memory that is in the process of becoming free:
645  global_bytesize desired_free = total_preemptive_needed + keep_free_for_user;
646  if ( currently_free < desired_free ) {
647  global_bytesize try_free = desired_free - currently_free;
648  swapOut ( try_free );
649  }
650 }
651 
652 
654 {
655  rambrain_pthread_mutex_lock ( &stateChangeMutex );
656  rambrain_pthread_mutex_lock ( &cyclicTopoLock );
657  BACKLOG_ADD_SIZE ( CHECK, 0 )
658 #ifdef PARENTAL_CONTROL
659  unsigned int no_reg = memChunks.size() - 1;
660 #else
661  unsigned int no_reg = memChunks.size();
662 #endif
663  unsigned int encountered = 0;
664  cyclicAtime *cur = active;
665  cyclicAtime *oldcur;
666 
667  global_bytesize usedBytes = 0;
668  global_bytesize tobef = 0;
669 
670  global_bytesize swappedBytes = 0;
671 
672  if ( !cur ) {
673  rambrain_pthread_mutex_unlock ( &cyclicTopoLock );
674  rambrain_pthread_mutex_unlock ( &stateChangeMutex );
675  if ( no_reg == 0 && counterActive == NULL ) {
676  return true;
677  } else {
678  errmsg ( "No active element found" );
679  return false;
680  }
681  }
682 
683  bool inActiveOnlySection = ( active->chunk->status == MEM_SWAPPED || active->chunk->status == MEM_SWAPOUT ? false : true );
684  bool inSwapsection = true;
685  bool memerror = false;
686  bool hasPreemptives = false;
687  do {
688  ++encountered;
689  oldcur = cur;
690  cur = cur->next;
691  if ( cur->chunk->preemptiveLoaded ) {
692  hasPreemptives = true;
693  }
694  if ( cur->chunk->status & MEM_ALLOCATED || cur->chunk->status == MEM_SWAPIN ) {
695  usedBytes += cur->chunk->size;
696  } else if ( cur->chunk->status == MEM_SWAPOUT ) {
697  tobef += cur->chunk->size;
698  usedBytes += cur->chunk->size;
699  }
700  if ( cur->chunk->status == MEM_SWAPPED || cur->chunk->status == rambrain::MEM_SWAPIN || cur->chunk->status == MEM_SWAPOUT ) {
701  swappedBytes += cur->chunk->size;
702  }
703 
704  if ( oldcur != cur->prev ) {
705  errmsgf ( "Mutual connecion failure at chunks %lu and %lu", oldcur->chunk->id, cur->chunk->id );
706  memerror = true;
707  }
708 
709  if ( inActiveOnlySection ) {
710  if ( oldcur->chunk->status == MEM_SWAPPED || oldcur->chunk->status == MEM_SWAPOUT ) {
711  errmsg ( "Swapped elements in active section!" );
712  memerror = true;
713  }
714  } else {
715  if ( oldcur->chunk->status == MEM_SWAPPED && !inSwapsection ) {
716  errmsg ( "Isolated swapped element block not tracked by counterActive found!" );
717  memerror = true;
718  }
719  if ( oldcur->chunk->status != MEM_SWAPPED && oldcur->chunk->status != MEM_SWAPOUT ) {
720  inSwapsection = false;
721  }
722  }
723 
724  if ( oldcur == counterActive ) {
725  inActiveOnlySection = false;
726  }
727 
728  } while ( cur != active );
729 
730  if ( usedBytes != memory_used ) {
731  errmsgf ( "Used bytes are not counted correctly, claimed %lu but found %lu", memory_used, usedBytes );
732  memerror = true;
733  }
734  if ( tobef != memory_tobefreed ) {
735  errmsgf ( "To-Be-Freed bytes are not counted correctly, claimed %lu but found %lu", memory_tobefreed, tobef );
736  memerror = true;
737  }
738  if ( swappedBytes != swap->getUsedSwap() ) {
739  errmsgf ( "Swapped bytes are not counted correctly, claimed %lu but found %lu", swap->getUsedSwap(), swappedBytes );
740  memerror = true;
741  }
742 
743 
744  unsigned int illicit_count = 0;
745  if ( preemptiveStart ) {
746  cur = preemptiveStart;
747  hasPreemptives = true;
748  while ( cur != active ) {
749  if ( cur->chunk->preemptiveLoaded == false ) {
750  if ( illicit_count < 10 ) {
751  errmsgf ( "Chunk %ld is a illicit non-preemptive.", cur->chunk->id );
752  } else {
753  if ( illicit_count == 10 ) {
754  errmsg ( "will not report further illicits" );
755  }
756  }
757  ++illicit_count;
758  hasPreemptives = false;
759  }
760  cur = cur->next;
761  }
762  if ( !hasPreemptives ) {
763  errmsg ( "We encountered non-preemptive elements in section marked as preemptive" );
764  memerror = true;
765  }
766  hasPreemptives = false;
767  illicit_count = 0;
768  while ( cur != preemptiveStart ) {
769  if ( cur->chunk->preemptiveLoaded == true ) {
770  if ( illicit_count < 10 ) {
771  errmsgf ( "Chunk %ld is a illicit preemptive.", cur->chunk->id );
772  } else {
773  if ( illicit_count == 10 ) {
774  errmsg ( "will not report further illicits" );
775  }
776  }
777  ++illicit_count;
778  hasPreemptives = true;
779  }
780  cur = cur->next;
781  }
782  if ( hasPreemptives ) {
783  errmsg ( "We encountered preemptive elements in section marked as non-preemptive" );
784  memerror = true;
785  }
786 
787  } else {
788  if ( hasPreemptives ) {
789  errmsg ( "We have preemptives but no preemptiveStart is assigned" );
790  memerror = true;
791  }
792 
793  }
794 
795  rambrain_pthread_mutex_unlock ( &cyclicTopoLock );
796  rambrain_pthread_mutex_unlock ( &stateChangeMutex );
797  if ( encountered != no_reg ) {
798  errmsgf ( "Not all elements accessed. %d expected, %d encountered", no_reg, encountered );
799  memerror = true;
800  }
801 
802  if ( memerror ) {
803  printBacklog();
804  printCycle();
805  return false;
806 
807  } else {
808  return true;
809  }
810 
811 }
812 
814 {
816 
817  if ( memChunks.size() <= 0 ) {
818  infomsg ( "No objects." );
819  return;
820  }
821  printf ( "%lu (%s)<-counterActive\n", counterActive->chunk->id, ( counterActive->chunk->preemptiveLoaded ? "p" : " " ) );
822  if ( preemptiveStart ) {
823  printf ( "%lu (%s)<-preemptiveStart\n", preemptiveStart->chunk->id, ( preemptiveStart->chunk->preemptiveLoaded ? "p" : " " ) );
824  }
825  printf ( "%lu => %lu => %lu\n", counterActive->prev->chunk->id, counterActive->chunk->id, counterActive->next->chunk->id );
826  printf ( "%lu (%s)<-active\n", active->chunk->id, ( active->chunk->preemptiveLoaded ? "p" : " " ) );
827  printf ( "%lu => %lu => %lu\n", active->prev->chunk->id, active->chunk->id, active->next->chunk->id );
828  printf ( "\n" );
829  do {
830  char status[2];
831  status[1] = 0x00;
832  switch ( atime->chunk->status ) {
833  case MEM_ALLOCATED:
834  if ( atime->chunk->useCnt > 0 ) {
835  status[0] = 'a'; //Small letters means that usage is already claimed.
836  } else {
837  status[0] = 'A';
838  }
839  break;
840  case MEM_SWAPIN:
841  if ( atime->chunk->useCnt > 0 ) {
842  status[0] = 'i'; //Small letters means that usage is already claimed.
843  } else {
844  status[0] = 'I';
845  }
846  break;
847  case MEM_SWAPOUT:
848  status[0] = 'O';
849  break;
850  case MEM_SWAPPED:
851  status[0] = 'S';
852  break;
853  case MEM_ALLOCATED_INUSE:
854  status[0] = '?';
855  break;
857  status[0] = 'W';
858  break;
860  status[0] = 'U';
861  break;
862  case MEM_ROOT:
863  status[0] = 'R';
864  break;
865  }
866  if ( atime == counterActive ) {
867  printf ( "%lu (%s) %u%s <-counterActive\t", atime->chunk->id, ( atime->chunk->preemptiveLoaded ? "p" : " " ), atime->chunk->useCnt, status );
868  } else {
869  printf ( "%lu (%s) %u%s \t", atime->chunk->id, ( atime->chunk->preemptiveLoaded ? "p" : " " ), atime->chunk->useCnt, status );
870  }
871  atime = atime->next;
872  } while ( atime != active );
873  printf ( "In Preemptive: %lu ( of %lf )\n", preemptiveBytes , memory_max * ( swapInFrac - swapOutFrac ) );
874  printf ( "\n" );
875  printf ( "\n" );
876 }
877 
878 void cyclicManagedMemory::printChain ( const char *name, const cyclicManagedMemory::chain mchain )
879 {
880  cyclicAtime *cur = mchain.from;
881  if ( !cur ) {
882  printf ( "Chain named '%s' is empty\n", name );
883  return;
884  }
885 
886  printf ( "Chain named '%s'", name );
887  while ( true ) {
888  printf ( "->%lu\t", cur->chunk->id );
889  if ( cur == mchain.to ) {
890  break;
891  }
892  cur = cur->next;
893  }
894  printf ( "\n" );
895 }
896 
897 
899 {
900  BACKLOG_ADD_SIZE ( SWAPOUT, min_size )
901  rambrain_pthread_mutex_lock ( &cyclicTopoLock );
902  if ( counterActive == 0 ) {
903  rambrain_pthread_mutex_unlock ( &stateChangeMutex );
904  rambrain_pthread_mutex_unlock ( &cyclicTopoLock );
905  Throw ( memoryException ( "I can't swap out anything if there's nothing to swap out." ) );
906  }
907  VERBOSEPRINT ( "swapOutEntry" );
908  if ( min_size > memory_max ) {
909  rambrain_pthread_mutex_unlock ( &cyclicTopoLock );
910  return ERR_MORETHANTOTALRAM;
911  }
912  global_bytesize swap_free = swap->getFreeSwap();
913  if ( swap_free < min_size ) {
914  swap->cleanupCachedElements ( min_size - swap_free );
915  swap_free = swap->getFreeSwap();
916  if ( swap_free < min_size ) {
917  if ( !swap->extendSwapByPolicy ( min_size - swap_free ) ) {
918  rambrain_pthread_mutex_unlock ( &cyclicTopoLock );
919  return ERR_SWAPFULL;
920  }
921  swap_free = swap->getFreeSwap();
922  }
923 
924  }
925 
926  global_bytesize mem_alloc_max = memory_max * swapOutFrac; //<- This is target size
927  global_bytesize mem_swap_min = memory_used > mem_alloc_max ? memory_used - mem_alloc_max : 0;
928  global_bytesize mem_swap = mem_swap_min < min_size ? min_size : mem_swap_min; // swap at least what you have to
929 
930  mem_swap = mem_swap > swap_free ? min_size : mem_swap;//But do not swap more than swap can take (Or try with only min_size)
931 
932  cyclicAtime *fromPos = counterActive;
933  cyclicAtime *countPos = counterActive;
934  global_bytesize unload_size = 0;
935  global_bytesize unload_size2 = 0;
936  unsigned int passed = 0, unload = 0;
937 #ifdef PARENTAL_CONTROL
938  unsigned int allelements = memChunks.size() - 1 ;
939 #else
940  unsigned int allelements = memChunks.size();
941 #endif
942 
943 
944  //First round: Calculate number of objects to swap out.
945  while ( unload_size < mem_swap ) {
946  ++passed;
947  if ( countPos->chunk->status == MEM_ALLOCATED && ( unload_size + countPos->chunk->size <= swap_free ) && ( countPos->chunk->useCnt == 0 ) ) {
948  if ( countPos->chunk->size + unload_size <= swap_free ) {
949  unload_size += countPos->chunk->size;
950  ++unload;
951 #ifdef VERYVERBOSE
952  printf ( "U(%d)\t", countPos->chunk->id );
953 #endif
954  }
955  }
956 
957  if ( passed == allelements ) {
958 #ifdef VERYVERBOSE
959  printf ( "emergency, once round!\n" );
960 #endif
961  break;
962  }
963 
964  countPos = countPos->prev;
965  }
966  if ( unload_size == 0 ) {
967 #ifdef VERYVERBOSE
968  printf ( "no candidates found\n" );
969 #endif
970  rambrain_pthread_mutex_unlock ( &cyclicTopoLock );
972  }
973 
974  managedMemoryChunk **unloadlist = new managedMemoryChunk*[unload];
975  managedMemoryChunk **unloadElem = unloadlist;
976 #ifdef VERYVERBOSE
977  printf ( "active = %d\n", active->chunk->id );
978 #endif
979  passed = 0;
980  bool resetPreemptiveStart = false;
981  while ( unload_size2 < unload_size && passed != allelements ) {
982  ++passed;
983  if ( fromPos->chunk->status == MEM_ALLOCATED && ( unload_size2 + fromPos->chunk->size <= swap_free ) && ( fromPos->chunk->useCnt == 0 ) ) {
984  if ( fromPos->chunk->size + unload_size2 <= swap_free ) {
985  unload_size2 += fromPos->chunk->size;
986  *unloadElem = fromPos->chunk;
987  ++unloadElem;
988 #ifdef VERYVERBOSE
989  printf ( "swapout %d\n", fromPos->chunk->id );
990 #endif
991  if ( fromPos->chunk->preemptiveLoaded ) { //We had this chunk preemptive, but now have to swap out.
992  //This is a bit evil, as we will reload preemptive bytes when we've swapped them out.
994  fromPos->chunk->preemptiveLoaded = false;
995  preemptiveBytes -= fromPos->chunk->size;
996  }
997  if ( preemptiveStart && ( fromPos->chunk == preemptiveStart->chunk ) ) {
998  resetPreemptiveStart = true;
999  }
1000  if ( active->chunk == fromPos->chunk ) {
1001  active = active->prev;
1002  }
1003  }
1004  }
1005  fromPos = fromPos->prev;
1006  }
1007  global_bytesize real_unloaded = swap->swapOut ( unloadlist, unload );
1008  delete[] unloadlist;
1009  bool swapSuccess = ( real_unloaded >= mem_swap ) ; // Do not compare with unload size (false positives!)
1010  if ( !swapSuccess ) {
1011  if ( real_unloaded == 0 ) {
1012  rambrain_pthread_mutex_unlock ( &cyclicTopoLock );
1013 #ifdef VERYVERBOSE
1014  printf ( "Swap out did not work (%lu vs %lu )\n", min_size, real_unloaded );
1015 #endif
1016  return ERR_NOTENOUGHCANDIDATES;
1017  }
1018  }
1019 #ifdef VERYVERBOSE
1020  printf ( "active = %d\n", active->chunk->id );
1021 #endif
1022  fromPos = fromPos->next;
1023  chain possiblyContaminated = {fromPos, counterActive};
1024  cyclicAtime *after = counterActive->next;
1025  if ( after == fromPos ) { // We're cyclic
1026  after = NULL;
1027  }
1028  memoryStatus notAllowed[] = {MEM_SWAPOUT, MEM_SWAPPED, MEM_ROOT};
1029  counterActive = possiblyContaminated.from->prev;
1030  chain filtered = filterChain ( possiblyContaminated, notAllowed );
1031  //Possibilities:
1032  if ( !possiblyContaminated.from ) { // filtered out all selected
1033  if ( after ) { //However, there are still other elements around.
1034  insertBefore ( after, filtered );
1035 
1036 
1037  } else { //We're left with what we have
1038  MUTUAL_CONNECT ( filtered.to, filtered.from );
1040  }
1041 
1042  } else {
1043  //We will have filtered out some of them.
1044  if ( !after ) {
1045  after = possiblyContaminated.to->next;
1046  }
1047  insertBefore ( after, filtered );
1048  counterActive = filtered.from->prev;
1049  }
1050  if ( active->chunk->preemptiveLoaded ) {
1051 #ifdef VERYVERBOSE
1052  printf ( "had to move active for preemptive\n" );
1053 #endif
1054  active = active->next;
1055  } else {
1056  if ( ! ( active->chunk->status & MEM_ALLOCATED || active->chunk->status == MEM_SWAPIN ) ) { // We may have swapped out the first allocated element
1058 #ifdef VERYVERBOSE
1059  printf ( "had to move active\n" );
1060 #endif
1061  }
1062  }
1063  if ( resetPreemptiveStart ) { //Rare case!
1064  cyclicAtime *cur = active;
1065 
1066  while ( cur->prev->chunk->preemptiveLoaded ) {
1067  cur = cur->prev;
1068  }
1069  preemptiveStart = ( cur == active ? NULL : cur );
1070  }
1071  rambrain_pthread_mutex_unlock ( &cyclicTopoLock );
1072  VERBOSEPRINT ( "swapOutReturn" );
1073 #ifdef SWAPSTATS
1074  swap_out_scheduled_bytes += real_unloaded;
1075  n_swap_out += 1;
1076 #endif
1077  if ( swapSuccess ) {
1078 #ifdef VERYVERBOSE
1079  printf ( "Swap out succeeded (%lu vs %lu )\n", min_size, real_unloaded );
1080 #endif
1081 
1082  return ERR_SUCCESS;
1083  } else {
1084 #ifdef VERYVERBOSE
1085  printf ( "Swap out (%lu vs %lu )\n", min_size, real_unloaded );
1086 #endif
1087  if ( real_unloaded >= min_size ) {
1088  return ERR_SUCCESS;
1089  } else {
1090  return ERR_NOTENOUGHCANDIDATES;
1091  }
1092  }
1093 }
1094 
1096 {
1097  auto it = memChunks.begin();
1098  while ( it != memChunks.end() ) {
1099  cyclicAtime *element = ( cyclicAtime * ) it->second->schedBuf;
1100  if ( element ) {
1101  delete element;
1102  }
1103  ++it;
1104  }
1105 }
1107 {
1108  printf ( "Begin backlog\n" );
1109  unsigned int pos = backlog_pos;
1110  do {
1111  switch ( backlog[pos].action ) {
1112  case SWAPIN:
1113  printf ( "SWAPIN\t of chunk %lu\n", backlog[pos].value.id );
1114  break;
1115  case SWAPOUT:
1116  printf ( "SWAPOUT\t of %lu bytes\n", backlog[pos].value.size );
1117  break;
1118  case DELETE:
1119  printf ( "DELETION\t of chunk %lu\n", backlog[pos].value.id );
1120  break;
1121  case REGISTER:
1122  printf ( "REGISTER\t of chunk %lu\n", backlog[pos].value.id );
1123  break;
1124  case TOUCH:
1125  printf ( "TOUCH\t of chunk %lu\n", backlog[pos].value.id );
1126  break;
1127  case DECAY:
1128  printf ( "DECAY\t of %lu Bytes\n", backlog[pos].value.size );
1129  break;
1130  case CHECK:
1131  printf ( "CHECKED cycle\n" );
1132  case UNKNOWN:
1133  ;
1134  };
1135  pos = ( pos + 1 ) % backlog_size;
1136  } while ( backlog_pos != pos );
1137 
1138  printf ( "End backlog\n" );
1139 }
1140 
1141 }
1142 
Exception for errors with the memory.
Definition: exceptions.h:87
structure created by scheduler to track access times of memoryChunks
cyclicAtime * next
The chunk.
virtual bool extendSwapByPolicy(global_bytesize min_size)
extend swap by policy
Definition: managedSwap.h:69
static void printChain(const char *name, const chain mchain)
Swapping action was successful.
memoryID id
an ID to identify the object in scheduler or elsewhere
virtual void schedulerRegister(managedMemoryChunk &chunk)
gives scheduler code the opportunity to register its own datastructures associated with a chunk ...
static bool Throw(memoryException e)
Custom throw function, as we need to prevent throwing exceptions in construtors.
static void insertBefore(cyclicAtime *pos, chain separated)
void printMemUsage() const
prints out current memory usage
virtual global_bytesize getUsedSwap() const
Simple getter.
Definition: managedSwap.h:86
Class that serves as a backend to managedMemory to actual write/read managedMemoryChunks to/from hard...
Definition: managedSwap.h:35
bool setPreemptiveLoading(bool preemptive)
sets whether scheduler should guess next needed items
global_bytesize swap_out_scheduled_bytes
void * schedBuf
a place to store additional scheduling information
virtual bool swapIn(managedMemoryChunk &chunk)
cyclic implementation of swapIn, see paper
virtual global_bytesize getSwapSize() const
Simple getter.
Definition: managedSwap.h:82
global_bytesize memory_tobefreed
global_bytesize n_swap_out
virtual void untouch(managedMemoryChunk &chunk)
tries to regulate immediately usable free memory in ram to a level optimal for preemptive loading ...
bool checkCycle()
checks whether the scheduler believes to be sane and prints an error message if not ...
uint64_t global_bytesize
Definition: common.h:65
struct cyclicManagedMemory::chain filterChain(chain &toFilter, const memoryStatus *separateStatus, bool *preemptiveLoaded=NULL)
: separates all chunks matching state in list separateStatus or preeemptiveLoaded from the ring ...
static pthread_mutex_t cyclicTopoLock
virtual global_bytesize swapIn(managedMemoryChunk **chunklist, unsigned int nchunks)=0
Trigger swap in of the chunks pointed to by chunklist.
global_bytesize min_elements
virtual global_bytesize getFreeSwap() const
Simple getter.
Definition: managedSwap.h:90
virtual swapErrorCode swapOut(global_bytesize min_size)
cyclic implementation of swapOut, see paper
global_bytesize n_swap_in
global_bytesize swap_in_scheduled_bytes
void decay(global_bytesize bytes)
: Tries to unload around bytes bytes of preemptive elements
static pthread_mutex_t stateChangeMutex
bool ensureEnoughSpace(global_bytesize sizereq, managedMemoryChunk *orIsSwappedin=NULL)
This function ensures that there is sizereq space left in ram.
cyclicAtime * prev
Next chunk in cycle.
virtual void schedulerDelete(managedMemoryChunk &chunk)
signals deletion of chunk to scheduler code
manages all managed Chunks of raw memory
virtual global_bytesize swapOut(managedMemoryChunk **chunklist, unsigned int nchunks)=0
Trigger swap out of the chunks pointed to by chunklist.
global_bytesize memory_used
std::map< memoryID, managedMemoryChunk * > memChunks
virtual bool cleanupCachedElements(rambrain::global_bytesize minimum_size=0)
throws out cached elements still in ram but also resident on disk. This makes space in situations of ...
Definition: managedSwap.h:137
The element/size requested does not fit in RAM as a whole.
bool setPreemptiveUnloading(bool preemptive)
sets whether scheduler should swap out elements without strict need
We lack reasonable candidates for swapout (too much elements in use?)
void printCycle() const
prints out all elements known to the scheduler with their respective status
static const unsigned int backlog_size
bool waitForSwapin(managedMemoryChunk &chunk, bool keepSwapLock=false)
Waits until a certain chunk is present.
global_bytesize memory_max
global_bytesize memory_swapped
global_bytesize size
Size of actual object in bytes.
virtual bool touch(managedMemoryChunk &chunk)
marks chunk as recently active as a hint for scheduling
unsigned short useCnt
Number of using adhereTos or a possible location for locking the object to changes.
swapErrorCode
Error codes for swapOut requests.
managedMemoryChunk * chunk
backlog_entry backlog[backlog_size]
Backend class to handle raw memory and interaction/storage with managedSwap.
Definition: managedMemory.h:68
cyclicManagedMemory(rambrain::managedSwap *swap, rambrain::global_bytesize size)