HeapManager.Mod 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. (* Non-blocking heaps *)
  2. (* Copyright (C) Florian Negele *)
  3. (** The HeapManager module provides a lock-free data structure called Heap that handles memory management. *)
  4. (** A heap manages the allocation and deallocation of blocks of various sizes within a contiguous memory region. *)
  5. MODULE HeapManager;
  6. IMPORT CPU, Processors;
  7. (* The minimal size of a memory block *)
  8. CONST BlockSize = SIZE OF ADDRESS * 8;
  9. (* Representation of a memory block *)
  10. TYPE Block = POINTER {UNSAFE} TO RECORD index: SIZE; next {UNTRACED}: Block END;
  11. (** Represents a heap which manages a contiguous memory region. *)
  12. (** Heaps have to be initialised using the HeapManager.Initialize procedure before they are available for memory allocations. *)
  13. (* A heap is structured into a buddy system of memory blocks of various sizes. *)
  14. (* It maintains a free list for each different size of blocks. *)
  15. TYPE Heap* = RECORD
  16. free: ARRAY SIZE OF ADDRESS * 8 OF RECORD first {UNTRACED}, pooled {UNTRACED}: Block END;
  17. begin, end: ADDRESS; sentinel := 0: SIZE;
  18. END;
  19. VAR processors: ARRAY Processors.Maximum OF RECORD {ALIGNED (CPU.CacheLineSize)} hazard {UNTRACED}: Block END;
  20. (* Computes the index of a block that has the given size. *)
  21. PROCEDURE GetIndex (size: SIZE): SIZE;
  22. VAR result := 0: SIZE;
  23. BEGIN {UNCOOPERATIVE, UNCHECKED}
  24. size := (size - 1) DIV BlockSize;
  25. WHILE size # 0 DO INC (result); size := size DIV 2 END;
  26. RETURN result;
  27. END GetIndex;
  28. (** Initializes a heap that manages the memory region encompassed by the specified address range. *)
  29. (** The memory area must be owned by the caller and may not overlap with other heaps. *)
  30. (** This procedure must be called once before memory can be allocated from the corresponding heap. *)
  31. PROCEDURE Initialize- (VAR heap: Heap; begin, end: ADDRESS);
  32. VAR index: SIZE; next: ADDRESS; first {UNTRACED}: Block;
  33. BEGIN {UNCOOPERATIVE, UNCHECKED}
  34. (* check for valid arguments *)
  35. ASSERT (begin # NIL);
  36. ASSERT (end >= begin + BlockSize);
  37. (* setup heap size and align memory region *)
  38. begin := begin + (BlockSize - (begin + SIZE OF ADDRESS) MOD BlockSize) MOD BlockSize;
  39. end := begin + (end - begin) DIV BlockSize * BlockSize; heap.begin := begin; heap.end := end;
  40. (* setup all blocks and map the memory range as unallocated *)
  41. heap.sentinel := GetIndex (end - begin + BlockSize); index := heap.sentinel - 1;
  42. REPEAT
  43. LOOP
  44. next := begin + ASH (BlockSize, index);
  45. IF next <= end THEN EXIT END;
  46. DEC (index); heap.free[index].first := NIL;
  47. END;
  48. first := begin; first.next := heap.free[index].first;
  49. heap.free[index].first := first; begin := next;
  50. UNTIL begin = end;
  51. WHILE index # 0 DO DEC (index); heap.free[index].first := NIL END;
  52. REPEAT heap.free[index].pooled := NIL; INC (index) UNTIL index = heap.sentinel;
  53. END Initialize;
  54. PROCEDURE Access (VAR first {UNTRACED}: Block): {UNTRACED} Block;
  55. VAR index: SIZE; value {UNTRACED}: Block;
  56. BEGIN {UNCOOPERATIVE, UNCHECKED}
  57. value := CAS (first, NIL, NIL);
  58. index := Processors.GetCurrentIndex ();
  59. REPEAT
  60. processors[index].hazard := value;
  61. value := CAS (first, NIL, NIL);
  62. UNTIL processors[index].hazard = value;
  63. RETURN value;
  64. END Access;
  65. PROCEDURE Discard;
  66. BEGIN {UNCOOPERATIVE, UNCHECKED}
  67. processors[Processors.GetCurrentIndex ()].hazard := NIL;
  68. END Discard;
  69. PROCEDURE IsHazardous (block {UNTRACED}: Block): BOOLEAN;
  70. VAR index: SIZE;
  71. BEGIN {UNCOOPERATIVE, UNCHECKED}
  72. FOR index := 0 TO Processors.Maximum - 1 DO
  73. IF block = processors[index].hazard THEN RETURN TRUE END;
  74. END;
  75. RETURN FALSE;
  76. END IsHazardous;
  77. PROCEDURE Pool (block {UNTRACED}: Block; index: SIZE; VAR heap: Heap);
  78. VAR pooled {UNTRACED}: Block;
  79. BEGIN {UNCOOPERATIVE, UNCHECKED}
  80. LOOP
  81. pooled := CAS (heap.free[index].pooled, NIL, NIL); block.next := pooled;
  82. IF CAS (heap.free[index].pooled, pooled, block) = pooled THEN RETURN END;
  83. CPU.Backoff;
  84. END;
  85. END Pool;
  86. PROCEDURE Acquire (index: SIZE; VAR heap: Heap): {UNTRACED} Block;
  87. VAR first {UNTRACED}, value {UNTRACED}: Block;
  88. BEGIN {UNCOOPERATIVE, UNCHECKED}
  89. first := CAS (heap.free[index].pooled, NIL, NIL);
  90. IF (first # NIL) & (CAS (heap.free[index].pooled, first, NIL) = first) THEN
  91. REPEAT value := first.next; Release (first, index, heap); first := value UNTIL first = NIL;
  92. END;
  93. LOOP
  94. first := Access (heap.free[index].first);
  95. IF first = NIL THEN Discard; RETURN NIL END;
  96. value := CAS (heap.free[index].first, first, first.next);
  97. Discard; IF value = first THEN EXIT END;
  98. CPU.Backoff;
  99. END;
  100. RETURN first;
  101. END Acquire;
  102. PROCEDURE Release (block {UNTRACED}: Block; index: SIZE; VAR heap: Heap);
  103. VAR size: SIZE; buddy {UNTRACED}, first {UNTRACED}, value {UNTRACED}: Block;
  104. BEGIN {UNCOOPERATIVE, UNCHECKED}
  105. size := ASH (BlockSize, index);
  106. LOOP
  107. IF IsHazardous (block) THEN Pool (block, index, heap); RETURN END;
  108. LOOP
  109. first := Access (heap.free[index].first); IF first = NIL THEN Discard; EXIT END;
  110. IF ODD ((ADDRESS OF block^ - heap.begin) DIV size) THEN buddy := ADDRESS OF block^ - size ELSE buddy := ADDRESS OF block^ + size END;
  111. IF buddy # first THEN Discard; EXIT END; value := CAS (heap.free[index].first, first, first.next);
  112. Discard; IF value # first THEN first := value; EXIT END; INC (index); size := size * 2;
  113. IF ADDRESS OF buddy^ < ADDRESS OF block^ THEN block := buddy; IF IsHazardous (block) THEN Pool (block, index, heap); RETURN END END;
  114. END;
  115. block.next := first;
  116. IF CAS (heap.free[index].first, first, block) = first THEN EXIT END;
  117. CPU.Backoff;
  118. END;
  119. END Release;
  120. (** Allocates a block of memory with the requested size from the specified heap. *)
  121. (** The return value is the first address of the allocated memory, or NIL if the heap as no more free memory. *)
  122. PROCEDURE Allocate- (size: SIZE; VAR heap: Heap): ADDRESS;
  123. VAR index, current: SIZE; result: ADDRESS; block {UNTRACED}: Block;
  124. BEGIN {UNCOOPERATIVE, UNCHECKED}
  125. (* check for valid arguments *)
  126. ASSERT (size # 0);
  127. ASSERT (heap.sentinel # 0);
  128. index := GetIndex (size + SIZE OF ADDRESS);
  129. IF index >= heap.sentinel THEN RETURN NIL END;
  130. current := index; size := ASH (BlockSize, index);
  131. LOOP
  132. result := Acquire (current, heap);
  133. IF result # NIL THEN EXIT END;
  134. INC (current); size := size * 2;
  135. IF current = heap.sentinel THEN RETURN NIL END;
  136. END;
  137. WHILE current # index DO
  138. DEC (current); size := size DIV 2;
  139. Release (result + size, current, heap);
  140. END;
  141. block := result; block.index := index;
  142. RETURN result + SIZE OF ADDRESS;
  143. END Allocate;
  144. (** Deallocates a memory block that was previously allocated using a call to the HeapManager.Allocate procedure. *)
  145. PROCEDURE Deallocate- (address: ADDRESS; VAR heap: Heap);
  146. VAR block {UNTRACED}: Block; index: SIZE;
  147. BEGIN {UNCOOPERATIVE, UNCHECKED}
  148. ASSERT (heap.sentinel # 0);
  149. ASSERT (IsValid (address, heap));
  150. block := address - SIZE OF ADDRESS; index := block.index;
  151. ASSERT (index < heap.sentinel);
  152. Release (block, index, heap);
  153. END Deallocate;
  154. (** Checks whether an address is a valid heap address. *)
  155. PROCEDURE IsValid- (address: ADDRESS; CONST heap: Heap): BOOLEAN;
  156. BEGIN {UNCOOPERATIVE, UNCHECKED} RETURN (address >= heap.begin + SIZE OF ADDRESS) & (address < heap.end) & ((address MOD BlockSize) = 0);
  157. END IsValid;
  158. (** Returns the size of an allocated block of memory. *)
  159. PROCEDURE GetSize- (address: ADDRESS; CONST heap: Heap): SIZE;
  160. VAR block {UNTRACED}: Block; index: SIZE;
  161. BEGIN {UNCOOPERATIVE, UNCHECKED}
  162. ASSERT (heap.sentinel # 0);
  163. ASSERT (IsValid (address, heap));
  164. block := address - SIZE OF ADDRESS; index := block.index;
  165. ASSERT (index < heap.sentinel);
  166. RETURN ASH (BlockSize, index);
  167. END GetSize;
  168. END HeapManager.