/pool_test.txt
/pool_test_gen
/pool_test_run
+/process_test.txt
+/process_test_gen
+/process_test_run
CFLAGS=-g -Wall
-all: pool_test_gen pool_test_run
+all: pool_test_gen pool_test_run process_test_gen process_test_run
pool_test_gen: pool_test_gen.o
pool_test_gen.o: pool_test_gen.c
pool.o: pool.c pool.h
+process_test_gen: process_test_gen.o
+process_test_gen.o: process_test_gen.c
+
+process_test_run: process_test_run.o process.o core.o swap.o pool.o
+process_test_run.o: process_test_run.c process.h core.h swap.h pool.h
+
+process.o: process.c process.h core.h swap.h pool.h
+core.o: core.c core.h pool.h
+swap.o: swap.c swap.h core.h pool.h
+
clean:
- rm -f pool_test_gen pool_test_run *.o
+ rm -f pool_test_gen pool_test_run process_test_gen process_test_run *.o
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "core.h"
+#include "rassert.h"
+
+struct pool_head core_head;
+int *core_mem;
+
+void core_move(struct pool_item *item, int new_base) {
+ int base = item->base;
+ int size = item->limit - base;
+ printf(
+ "core_move [%d,%d) to [%d,%d)\n",
+ base,
+ base + size,
+ new_base,
+ new_base + size
+ );
+ assert(new_base < base || new_base >= base + size);
+ for (int i = 0; i < size; ++i) {
+ core_mem[new_base + i] = core_mem[base + i];
+ core_mem[base + i] = 0xaaaaaaaa;
+ }
+}
+
+void core_move_up(struct pool_item *item, int new_limit) {
+ int limit = item->limit;
+ assert(limit != new_limit);
+ int neg_size = item->base - limit;
+ printf(
+ "core_move_up [%d,%d) to [%d,%d)\n",
+ limit + neg_size,
+ limit,
+ new_limit + neg_size,
+ new_limit
+ );
+ assert(new_limit > limit || new_limit <= limit + neg_size);
+ for (int i = -1; i >= neg_size; --i) {
+ core_mem[new_limit + i] = core_mem[limit + i];
+ core_mem[limit + i] = 0xaaaaaaaa;
+ }
+}
+
+void core_init(int core, int spare) {
+ pool_init(&core_head, 0, core, spare, core_move, core_move_up);
+
+ core_mem = malloc(core * sizeof(int));
+ memset(core_mem, 0xaa, core * sizeof(int));
+}
--- /dev/null
+#ifndef _CORE_H
+#define _CORE_H 1
+
+#include "pool.h"
+
+extern struct pool_head core_head;
+extern int *core_mem;
+
+void core_init(int core, int spare);
+
+#endif
#!/bin/sh
echo "generate test script"
-./pool_test_gen 64 256 256 16 >pool_test.txt
+./pool_test_gen 64 240 256 16 >pool_test.txt
echo "run non-moveable test"
-./pool_test_run 64 256 <pool_test.txt
+./pool_test_run 64 256 16 <pool_test.txt
echo "run moveable test"
-./pool_test_run 64 256 1 <pool_test.txt
+./pool_test_run 64 256 16 1 <pool_test.txt
--- /dev/null
+#!/bin/sh
+
+echo "generate test script"
+./process_test_gen 16 224 1024 32 >process_test.txt
+
+echo "run test"
+./process_test_run 16 64 4 192 12 16 <process_test.txt
#define TRY_BLOCKER 1
#define TRY_REALLOC 1
+#if 1
+void check_invariants(struct pool_head *head) {
+ int avail;
+ struct pool_item *p;
+
+ avail = head->item.base - head->item.limit - head->spare;
+ for (p = head->item.next; p != &head->item; p = p->next) {
+ assert(p->prev->next == p && p->next->prev == p);
+ assert(p->base >= p->prev->limit && p->limit <= p->next->base);
+ avail += p->base - p->limit;
+ }
+ assert(avail == head->avail);
+}
+#endif
+
void pool_init(
struct pool_head *head,
int base,
int limit,
+ int spare,
void (*move)(struct pool_item *item, int new_base),
void (*move_up)(struct pool_item *item, int new_limit)
) {
head->item.next = &head->item;
head->item.base = limit; // note: swapped
head->item.limit = base; // note: swapped
- head->avail = limit - base;
+ head->avail = limit - base - spare;
+ head->spare = spare;
head->move = move;
head->move_up = move_up;
+ check_invariants(head);
}
bool pool_alloc(
// item must not be allocated yet
assert(item->prev == NULL && item->next == NULL);
+ // check size, disregarding fragmentation
+ avail = head->avail - size;
+ if (avail < 0)
+ return false;
+ avail += head->spare;
+
// first-fit algorithm
q = &head->item;
- for (avail = head->avail - size; avail >= 0; avail -= gap) {
+ do {
p = q;
q = p->next;
gap = q->base - p->limit;
goto found;
if (q == &head->item)
break;
- }
+ avail -= gap;
+ } while (avail >= 0);
return false;
found:
// keep track of total allocation
head->avail -= size;
-#ifndef NDEBUG
- for (struct pool_item *r = head->item.next; r != &head->item; r = r->next) {
- assert(r->prev->next == r && r->next->prev == r);
- assert(r->base >= r->prev->limit && r->limit <= r->next->base);
- }
-#endif
+ check_invariants(head);
return true;
}
if (item->base + size <= item->next->base)
goto resize;
+ // check size, disregarding fragmentation
+ avail = head->avail - size_change;
+ if (avail < 0)
+ return false;
+ avail += head->spare;
+
// first-fit algorithm, until our own list entry
q = &head->item;
- for (avail = head->avail - size_change; avail >= 0; avail -= gap) {
+ do {
p = q;
q = p->next;
if (q == item)
if (size <= gap)
goto found;
assert(q != &head->item);
- }
+ avail -= gap;
+ } while (avail >= 0);
return false;
ourself:
// the first iteration (ours) is different because q != p->next,
// this causes us to ignore our own entry in the gap calculation
q = q->next;
- while (true) {
+ goto loop_entry;
+ do {
+ p = q;
+ q = p->next;
+ loop_entry:
gap = q->base - p->limit;
if (size <= gap)
goto found;
if (q == &head->item)
break;
avail -= gap;
- if (avail < 0)
- break;
- p = q;
- q = p->next;
- }
+ } while (avail >= 0);
return false;
found:
// keep track of total allocation
head->avail -= size_change;
-#ifndef NDEBUG
- for (struct pool_item *r = head->item.next; r != &head->item; r = r->next) {
- assert(r->prev->next == r && r->next->prev == r);
- assert(r->base >= r->prev->limit && r->limit <= r->next->base);
- }
-#endif
+ check_invariants(head);
return true;
}
// keep track of total allocation
head->avail += item->limit - item->base;
+ check_invariants(head);
}
bool pool_alloc_moveable(
// item must not be allocated yet
assert(item->prev == NULL && item->next == NULL);
- // first-fit algorithm
+ // check size, disregarding fragmentation
avail = head->avail - size;
if (avail < 0)
return false;
+ avail += head->spare;
+
+ // first-fit algorithm
q = &head->item;
while (true) {
p = q;
// keep track of total allocation
head->avail -= size;
-#ifndef NDEBUG
- for (struct pool_item *r = head->item.next; r != &head->item; r = r->next) {
- assert(r->prev->next == r && r->next->prev == r);
- assert(r->base >= r->prev->limit && r->limit <= r->next->base);
- }
-#endif
+ check_invariants(head);
return true;
}
goto resize;
#endif
- // first-fit algorithm, until our own list entry
+ // check size, disregarding fragmentation
avail = head->avail - size_change;
if (avail < 0)
return false;
+ avail += head->spare;
+
+ // first-fit algorithm, until our own list entry
q = &head->item;
#ifdef TRY_BLOCKER
#endif
while (true) {
- for (struct pool_item *r = head->item.next; r != &head->item; r = r->next) {
- assert(r->prev->next == r && r->next->prev == r);
- assert(r->base >= r->prev->limit && r->limit <= r->next->base);
- }
p = q;
q = p->next;
if (q == item)
#ifdef TRY_BLOCKER
// see if blocker fits in gap
- if (blocker_size <= gap && blocker != q) {
+ if (blocker_size <= gap) {
// if moveable realloc is used, then user must provide move routine
assert(head->move);
head->move(blocker, p->limit);
// calculate new blocker
q = blocker;
- for (struct pool_item *r = head->item.next; r != &head->item; r = r->next) {
- assert(r->prev->next == r && r->next->prev == r);
- assert(r->base >= r->prev->limit && r->limit <= r->next->base);
- }
goto blocker_test;
}
#endif
// the first iteration (ours) is different because q != p->next,
// this causes us to ignore our own entry in the gap calculation
q = q->next;
- while (true) {
- for (struct pool_item *r = head->item.next; r != &head->item; r = r->next) {
- assert(r->prev->next == r && r->next->prev == r);
- assert(r->base >= r->prev->limit && r->limit <= r->next->base);
- }
+ goto loop_entry;
+ do {
+ p = q;
+ q = p->next;
+ loop_entry:
gap = q->base - p->limit;
if (size <= gap)
goto found;
if (q == &head->item)
break;
avail -= gap;
- if (avail < 0)
- break;
- p = q;
- q = p->next;
- }
+ } while (avail >= 0);
avail = avail_save;
p = item->prev;
// calculate gap that would be left if we didn't move ourself,
// if too large, move block after gap (ourself) down into gap
- for (struct pool_item *r = head->item.next; r != &head->item; r = r->next) {
- assert(r->prev->next == r && r->next->prev == r);
- assert(r->base >= r->prev->limit && r->limit <= r->next->base);
- }
gap = item->base - p->limit;
avail -= gap;
if (avail < 0) {
// soon want to resize again), compact the remainder down from top
p = &head->item;
while (true) {
- for (struct pool_item *r = head->item.next; r != &head->item; r = r->next) {
- assert(r->prev->next == r && r->next->prev == r);
- assert(r->base >= r->prev->limit && r->limit <= r->next->base);
- }
q = p;
p = q->prev;
if (p == item) {
// keep track of total allocation
head->avail -= size_change;
-#ifndef NDEBUG
- for (struct pool_item *r = head->item.next; r != &head->item; r = r->next) {
- assert(r->prev->next == r && r->next->prev == r);
- assert(r->base >= r->prev->limit && r->limit <= r->next->base);
- }
-#endif
+ check_invariants(head);
return true;
}
struct pool_head {
struct pool_item item;
int avail;
+ int spare;
void (*move)(struct pool_item *item, int new_base);
void (*move_up)(struct pool_item *item, int new_limit);
};
struct pool_head *head,
int base,
int limit,
+ int spare,
void (*move)(struct pool_item *item, int new_base),
void (*move_up)(struct pool_item *item, int new_limit)
);
-#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-
-# define rassert(expr) \
- ((void) sizeof ((expr) ? 1 : 0), __extension__ ({ \
- if (expr) \
- ; /* empty */ \
- else \
- __assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION); \
- }))
+#include "rassert.h"
int rand_int(int n) {
return (int)((long long)rand() * n / (RAND_MAX + 1LL));
-#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pool.h"
-
-# define rassert(expr) \
- ((void) sizeof ((expr) ? 1 : 0), __extension__ ({ \
- if (expr) \
- ; /* empty */ \
- else \
- __assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION); \
- }))
-
-int rand_int(int n) {
- return (int)((long long)rand() * n / (RAND_MAX + 1LL));
-}
+#include "rassert.h"
int *mem;
new_base,
new_base + size
);
- rassert(new_base < base || new_base >= base + size);
+ assert(new_base < base || new_base >= base + size);
for (int i = 0; i < size; ++i) {
mem[new_base + i] = mem[base + i];
mem[base + i] = 0xaaaaaaaa;
new_limit + neg_size,
new_limit
);
- rassert(new_limit > limit || new_limit <= limit + neg_size);
+ assert(new_limit > limit || new_limit <= limit + neg_size);
for (int i = -1; i >= neg_size; --i) {
mem[new_limit + i] = mem[limit + i];
mem[limit + i] = 0xaaaaaaaa;
}
int main(int argc, char **argv) {
- if (argc < 3) {
- printf("usage: %s n_items pool_size [moveable]\n", argv[0]);
+ if (argc < 4) {
+ printf("usage: %s n_items pool_size spare [moveable]\n", argv[0]);
exit(EXIT_FAILURE);
}
int n_items = atoi(argv[1]);
int pool_size = atoi(argv[2]);
- bool moveable = argc >= 4 ? (bool)atoi(argv[3]) : false;
+ int spare = atoi(argv[3]);
+ bool moveable = argc >= 5 ? (bool)atoi(argv[4]) : false;
struct pool_head head;
- pool_init(&head, 0, pool_size, move, move_up);
+ pool_init(&head, 0, pool_size, spare, move, move_up);
struct pool_item *items = malloc(n_items * sizeof(struct pool_item));
rassert(items);
memset(mem, 0xaa, pool_size * sizeof(int));
while (true) {
- int avail = pool_size;
- for (struct pool_item *p = head.item.next; p != &head.item; p = p->next) {
- rassert(p->prev->next == p && p->next->prev == p);
- rassert(p->base >= p->prev->limit && p->limit <= p->next->base);
- avail += p->base - p->limit;
- }
- printf("avail %d\n", avail);
- rassert(avail == head.avail);
-
+ //printf("avail %d\n", head.avail);
char buf[256];
switch (scanf("%s", buf)) {
case -1:
rassert(item >= 0 && item < n_items);
rassert(size >= 0 && size < pool_size);
rassert(success >= 0 && success < 2);
- rassert(items[item].prev == NULL && items[item].next == NULL);
+ rassert(items[item].prev == NULL);
bool result =
moveable ?
pool_alloc_moveable(&head, items + item, size) :
if (!success) {
printf("undo\n");
pool_free(&head, items + item);
+ items[item].prev = NULL;
}
else {
int base = items[item].base;
rassert(old_size >= 0 && old_size < pool_size);
rassert(size >= 0 && size < pool_size);
rassert(success >= 0 && success < 2);
- if (items[item].prev == NULL && items[item].next == NULL)
+ if (items[item].prev == NULL)
printf(
"realloc %d %d %d %d: not allocated, ignore\n",
item,
rassert(scanf("%d %d", &item, &old_size) == 2);
rassert(item >= 0 && item < n_items);
rassert(old_size >= 0 && old_size < pool_size);
- if (items[item].prev == NULL && items[item].next == NULL)
+ if (items[item].prev == NULL)
printf(
"free %d %d: not allocated, ignore\n",
item,
old_size
);
else {
- printf("old region [%d,%d)\n", items[item].base, items[item].limit);
int actual_old_size = items[item].limit - items[item].base;
rassert(actual_old_size <= old_size);
pool_free(&head, items + item);
+ items[item].prev = NULL;
printf(
"free %d %d(%d): ok\n",
item,
actual_old_size
);
int base = items[item].base;
+ printf("old region [%d,%d)\n", base, base + actual_old_size);
for (int i = 0; i < actual_old_size; ++i) {
long long hash = item * 17 + i * 29;
hash = (hash & 0xffffffffffffffffLL) + (hash >> 32);
done:
printf("final state:\n");
for (int i = 0; i < n_items; ++i) {
- if (items[i].prev == NULL && items[i].next == NULL)
+ if (items[i].prev == NULL)
printf("item %d: not allocated\n", i);
else {
int base = items[i].base;
--- /dev/null
+#include <stdio.h> // temporary
+#include <stdlib.h>
+#include "core.h"
+#include "process.h"
+#include "rassert.h"
+#include "swap.h"
+
+struct process *processes;
+int n_processes;
+
+int process_avail, process_spare;
+
+struct process_item process_head_core, process_head_swap;
+
+#if 1
+static void check_invariants() {
+ int avail =
+ core_head.item.base -
+ core_head.item.limit -
+ core_head.spare +
+ swap_head.item.base -
+ swap_head.item.limit -
+ swap_head.spare -
+ process_spare;
+ for (int i = 0; i < n_processes; ++i)
+ if (processes[i].item.prev && processes[i].item.next) {
+ assert(
+ processes[i].size ==
+ (
+ processes[i].core_item.prev ?
+ processes[i].core_item.limit - processes[i].core_item.base :
+ 0
+ ) +
+ (
+ processes[i].swap_item.prev ?
+ processes[i].swap_item.limit - processes[i].swap_item.base :
+ 0
+ )
+ );
+ avail -= processes[i].size;
+ }
+ assert(avail == process_avail);
+
+ // in-core LRU list processes:
+ // must have core, can't have swap except the last
+ for (
+ struct process *p = (struct process *)process_head_core.next;
+ (struct process_item *)p != &process_head_core;
+ p = (struct process *)p->item.next
+ ) {
+ assert(p->item.prev->next == &p->item && p->item.next->prev == &p->item);
+ assert(p->core_item.prev);
+ assert(p->swap_item.prev == NULL || p->item.next == &process_head_core);
+ }
+
+ // in-swap LRU list processes:
+ // may have swap, can't have core
+ for (
+ struct process *p = (struct process *)process_head_swap.next;
+ (struct process_item *)p != &process_head_swap;
+ p = (struct process *)p->item.next
+ ) {
+ assert(p->item.prev->next == &p->item && p->item.next->prev == &p->item);
+ assert(p->core_item.prev == NULL);
+ }
+}
+#endif
+
+void process_init(int n, int spare) {
+ processes = calloc(n, sizeof(struct process));
+ rassert(processes);
+ n_processes = n;
+
+ process_avail = core_head.avail + swap_head.avail - spare;
+ process_spare = spare;
+
+ process_head_core.prev = &process_head_core;
+ process_head_core.next = &process_head_core;
+
+ process_head_swap.prev = &process_head_swap;
+ process_head_swap.next = &process_head_swap;
+ check_invariants();
+}
+
+static void do_swap_out(int swap_out) {
+ int size;
+ struct process *victim;
+
+ // while can still swap out, choose victim and swap some
+ for (; swap_out > 0; swap_out -= size) {
+ // choose victim and calculate how much can swap out,
+ // getting rid of victims that are completely swapped
+ goto loop_entry;
+ do {
+ printf("victimized %d\n", (int)(victim - processes));
+ // remove from tail of in-core LRU list
+ victim->item.prev->next = victim->item.next;
+ victim->item.next->prev = victim->item.prev;
+
+ // insert at head of in-swap LRU list
+ victim->item.prev = &process_head_swap;
+ victim->item.next = process_head_swap.next;
+ victim->item.prev->next = &victim->item;
+ victim->item.next->prev = &victim->item;
+
+ // remove from core pool for efficiency
+ // note: if zero length it's neither in core nor swap pool
+ pool_free(&core_head, &victim->core_item);
+ victim->core_item.prev = NULL;
+
+ loop_entry:
+ // there must be a victim, based on calculations above
+ victim = (struct process *)process_head_core.prev;
+ assert((struct process_item *)victim != &process_head_core);
+ size = victim->core_item.limit - victim->core_item.base;
+ } while (size == 0);
+
+ // if removed from swap pool for efficiency, put back in
+ if (victim->swap_item.prev == NULL)
+ rassert(pool_alloc_moveable(&swap_head, &victim->swap_item, 0));
+
+ // calculate amount to swap
+ if (size > swap_out)
+ size = swap_out;
+ printf("victim %d size %d\n", (int)(victim - processes), size);
+
+ // increase swap allocation
+ rassert(
+ pool_realloc_moveable(
+ &swap_head,
+ &victim->swap_item,
+ size + victim->swap_item.limit - victim->swap_item.base
+ )
+ );
+
+ // transfer data to swap
+ swap_push(
+ victim->core_item.limit,
+ victim->swap_item.limit - size,
+ size
+ );
+
+ // reduce core allocation
+ rassert(
+ pool_realloc_moveable(
+ &core_head,
+ &victim->core_item,
+ victim->core_item.limit - victim->core_item.base - size
+ )
+ );
+ }
+}
+
+bool process_alloc(struct process *process, int size) {
+ int swap_out;
+
+ // must not be already allocated
+ assert(process->item.prev == NULL && process->item.next == NULL);
+
+ // check size, disregarding fragmentation
+ if (process_avail < size)
+ return false;
+
+ // free up as much core as we need to
+ swap_out = size - core_head.avail;
+ assert(swap_out <= swap_head.avail);
+ do_swap_out(swap_out);
+
+ // allocate core, can't fail
+ rassert(pool_alloc_moveable(&core_head, &process->core_item, size));
+
+ // not allowed to be in swap
+ process->swap_item.prev = NULL;
+
+ // insert at head of in-core LRU list
+ process->item.prev = &process_head_core;
+ process->item.next = process_head_core.next;
+ process->item.prev->next = &process->item;
+ process->item.next->prev = &process->item;
+
+ // track total allocation
+ process->size = size;
+ process_avail -= size;
+ check_invariants();
+ return true;
+}
+
+bool process_realloc(struct process *process, int size) {
+ int swap_out;
+
+ // must be already allocated
+ assert(process->item.prev && process->item.next);
+
+ // must be fully in core
+ assert(process->swap_item.prev == NULL);
+
+ // check size, disregarding fragmentation
+ int size_change = size - process->size;
+ if (process_avail < size_change)
+ return false;
+
+ // free up as much core as we need to
+ swap_out = size_change - core_head.avail;
+ assert(swap_out <= swap_head.avail);
+ do_swap_out(swap_out);
+
+ // reallocate core, can't fail
+ rassert(pool_realloc_moveable(&core_head, &process->core_item, size));
+ process->size = size;
+
+ // track total allocation
+ process->size = size;
+ process_avail -= size_change;
+ check_invariants();
+ return true;
+}
+
+void process_run(struct process *process) {
+ int swap_in, swap_out;
+
+ // must be already allocated
+ assert(process->item.prev && process->item.next);
+
+ // remove from whichever LRU list it's in
+ process->item.prev->next = process->item.next;
+ process->item.next->prev = process->item.prev;
+
+ // insert at head of in-core LRU list
+ process->item.prev = &process_head_core;
+ process->item.next = process_head_core.next;
+ process->item.prev->next = &process->item;
+ process->item.next->prev = &process->item;
+
+ // if removed from core pool for efficiency, put back in
+ // note: this can only happen if it came from in-swap LRU list
+ if (process->core_item.prev == NULL)
+ rassert(pool_alloc_moveable(&core_head, &process->core_item, 0));
+
+ // if not in swap pool, then it must be fully in core
+ if (process->swap_item.prev == NULL)
+ return;
+
+ // while not fully in core, make space and bring some in
+ while (
+ (
+ swap_in =
+ process->size +
+ process->core_item.base -
+ process->core_item.limit
+ ) != 0
+ ) {
+ // free up as much core as we can, for this iteration
+ swap_out = swap_in - core_head.avail;
+ if (swap_out > swap_head.avail) {
+ swap_in += swap_head.avail - swap_out;
+ swap_out = swap_head.avail;
+ }
+ do_swap_out(swap_out);
+
+ // increase core allocation
+ rassert(
+ pool_realloc_moveable(
+ &core_head,
+ &process->core_item,
+ swap_in + process->core_item.limit - process->core_item.base
+ )
+ );
+
+ // transfer data to core
+ swap_pop(
+ process->swap_item.limit,
+ process->core_item.limit - swap_in,
+ swap_in
+ );
+
+ // reduce swap allocation
+ rassert(
+ pool_realloc_moveable(
+ &swap_head,
+ &process->swap_item,
+ process->swap_item.limit - process->swap_item.base - swap_in
+ )
+ );
+ }
+
+ // remove from swap pool for efficiency
+ pool_free(&swap_head, &process->swap_item);
+ process->swap_item.prev = NULL;
+ check_invariants();
+}
+
+void process_free(struct process *process) {
+ // must be already allocated
+ assert(process->item.prev && process->item.next);
+
+ // remove from whichever LRU list it's in
+ process->item.prev->next = process->item.next;
+ process->item.next->prev = process->item.prev;
+#ifndef NDEBUG
+ process->item.prev = NULL;
+ process->item.next = NULL;
+#endif
+
+ // free from core and/or swap pools if there
+ if (process->core_item.prev)
+ pool_free(&core_head, &process->core_item);
+ if (process->swap_item.prev)
+ pool_free(&swap_head, &process->swap_item);
+
+ // track total allocation
+ process_avail += process->size;
+ check_invariants();
+}
--- /dev/null
+#ifndef _PROCESS_H
+#define _PROCESS_H 1
+
+#include "pool.h"
+
+struct process_item {
+ struct process_item *prev;
+ struct process_item *next;
+};
+
+extern struct process {
+ struct process_item item; // must be first
+ struct pool_item core_item;
+ struct pool_item swap_item;
+ int size; // brk level
+} *processes;
+int n_processes;
+
+extern int process_avail, process_spare;
+
+extern struct process_item process_head_core, process_head_swap;
+
+void process_init(int n, int spare);
+bool process_alloc(struct process *process, int size);
+bool process_realloc(struct process *process, int size);
+void process_run(struct process *process);
+void process_free(struct process *process);
+
+#endif
--- /dev/null
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "rassert.h"
+
+int rand_int(int n) {
+ return (int)((long long)rand() * n / (RAND_MAX + 1LL));
+}
+
+int main(int argc, char **argv) {
+ if (argc < 5) {
+ printf("usage: %s n_processes pool_size n_events process_size [seed]\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ int n_processes = atoi(argv[1]);
+ int pool_size = atoi(argv[2]);
+ int n_events = atoi(argv[3]);
+ int process_size = atoi(argv[4]);
+ int seed = argc >= 6 ? atoi(argv[5]) : 1;
+
+ int *processes = malloc(n_processes * sizeof(int));
+ rassert(processes);
+ memset(processes, -1, n_processes * sizeof(int));
+
+ srand(seed);
+ int pool_used = 0;
+ for (int i = 0; i < n_events; ++i) {
+ int process = rand_int(n_processes);
+ int old_size = processes[process];
+ if (old_size == -1) {
+ int size = rand_int(process_size);
+ bool success = pool_used + size <= pool_size;
+ printf("alloc %d %d %d\n", process, size, success);
+ if (success) {
+ processes[process] = size;
+ pool_used += size;
+ }
+ }
+ else if (rand_int(64)) {
+ printf("run %d\n", process);
+ if (rand_int(8) == 0) {
+ int size = rand_int(process_size);
+ bool success = pool_used + size - old_size <= pool_size;
+ printf("realloc %d %d %d %d\n", process, old_size, size, success);
+ if (success) {
+ processes[process] = size;
+ pool_used += size - old_size;
+ }
+ }
+ }
+ else {
+ printf("free %d %d\n", process, old_size);
+ processes[process] = -1;
+ pool_used -= old_size;
+ }
+ }
+
+ return 0;
+}
--- /dev/null
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "core.h"
+#include "process.h"
+#include "rassert.h"
+#include "swap.h"
+
+int main(int argc, char **argv) {
+ if (argc < 7) {
+ printf("usage: %s n_processes core_size core_spare swap_size swap_spare spare\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ int n_processes = atoi(argv[1]);
+ int core_size = atoi(argv[2]);
+ int core_spare = atoi(argv[3]);
+ int swap_size = atoi(argv[4]);
+ int swap_spare = atoi(argv[5]);
+ int spare = atoi(argv[6]);
+
+ core_init(core_size, core_spare);
+ swap_init(swap_size, swap_spare);
+ process_init(n_processes, spare);
+
+ while (true) {
+ //printf("avail %d %d %d\n", process_avail, core_head.avail, swap_head.avail);
+ char buf[256];
+ switch (scanf("%s", buf)) {
+ case -1:
+ goto done;
+ case 1:
+ break;
+ default:
+ rassert(false);
+ }
+ if (strcmp(buf, "alloc") == 0) {
+ int process, size, success;
+ rassert(scanf("%d %d %d", &process, &size, &success) == 3);
+ rassert(process >= 0 && process < n_processes);
+ rassert(size >= 0 && size < core_size);
+ rassert(success >= 0 && success < 2);
+ rassert(processes[process].item.prev == NULL);
+ bool result = process_alloc(processes + process, size);
+ printf(
+ "alloc %d %d %d: %s\n",
+ process,
+ size,
+ success,
+ result == (bool)success ?
+ "ok" :
+ result ? "succeeded, should fail" : "failed, should succeed"
+ );
+ if (result) {
+ if (!success) {
+ printf("undo\n");
+ process_free(processes + process);
+ processes[process].item.prev = NULL;
+ }
+ else {
+ int base = processes[process].core_item.base;
+ printf("new core [%d,%d)\n", base, base + size);
+ rassert(processes[process].core_item.limit == base + size);
+ for (int i = 0; i < size; ++i) {
+ rassert(core_mem[base + i] == 0xaaaaaaaa);
+ long long hash = process * 17 + i * 29;
+ hash = (hash & 0xffffffffffffffffLL) + (hash >> 32);
+ hash = (hash & 0xffffffffffffffffLL) + (hash >> 32);
+ core_mem[base + i] = (int)hash;
+ }
+ }
+ }
+ }
+ else if (strcmp(buf, "realloc") == 0) {
+ int process, old_size, size, success;
+ rassert(scanf("%d %d %d %d", &process, &old_size, &size, &success) == 4);
+ rassert(process >= 0 && process < n_processes);
+ rassert(old_size >= 0 && old_size < core_size);
+ rassert(size >= 0 && size < core_size);
+ rassert(success >= 0 && success < 2);
+ if (processes[process].item.prev == NULL)
+ printf(
+ "realloc %d %d %d %d: not allocated, ignore\n",
+ process,
+ old_size,
+ size,
+ success
+ );
+ else {
+ printf("old core [%d,%d)\n", processes[process].core_item.base, processes[process].core_item.limit);
+ int actual_old_size = processes[process].size;
+ rassert(actual_old_size <= old_size);
+ bool result = process_realloc(processes + process, size);
+ printf(
+ "realloc %d %d(%d) %d %d: %s\n",
+ process,
+ old_size,
+ actual_old_size,
+ size,
+ success,
+ result == (bool)success ?
+ "ok" :
+ result ? "succeeded, should fail" : "failed, should succeed"
+ );
+ if (result) {
+ if (!success) {
+ printf("undo\n");
+ rassert(process_realloc(processes + process, actual_old_size));
+ }
+ else {
+ int base = processes[process].core_item.base;
+ printf("new core [%d,%d)\n", base, base + size);
+ rassert(processes[process].core_item.limit == base + size);
+ for (int i = actual_old_size; i < size; ++i) {
+ rassert(core_mem[base + i] == 0xaaaaaaaa);
+ long long hash = process * 17 + i * 29;
+ hash = (hash & 0xffffffffffffffffLL) + (hash >> 32);
+ hash = (hash & 0xffffffffffffffffLL) + (hash >> 32);
+ core_mem[base + i] = (int)hash;
+ }
+ for (int i = size; i < actual_old_size; ++i) {
+ long long hash = process * 17 + i * 29;
+ hash = (hash & 0xffffffffffffffffLL) + (hash >> 32);
+ hash = (hash & 0xffffffffffffffffLL) + (hash >> 32);
+ rassert(core_mem[base + i] == (int)hash);
+ core_mem[base + i] = 0xaaaaaaaa;
+ }
+ }
+ }
+ }
+ }
+ else if (strcmp(buf, "run") == 0) {
+ int process;
+ rassert(scanf("%d", &process) == 1);
+ rassert(process >= 0 && process < n_processes);
+ if (processes[process].item.prev == NULL)
+ printf("run %d: not allocated, ignore\n", process);
+ else {
+ process_run(processes + process);
+ printf("run %d\n", process);
+ }
+ }
+ else if (strcmp(buf, "free") == 0) {
+ int process, old_size;
+ rassert(scanf("%d %d", &process, &old_size) == 2);
+ rassert(process >= 0 && process < n_processes);
+ rassert(old_size >= 0 && old_size < core_size);
+ if (processes[process].item.prev == NULL)
+ printf(
+ "free %d %d: not allocated, ignore\n",
+ process,
+ old_size
+ );
+ else {
+ bool in_core = processes[process].core_item.prev != NULL;
+ bool in_swap = processes[process].swap_item.prev != NULL;
+ rassert(processes[process].size <= old_size);
+ process_free(processes + process);
+ processes[process].item.prev = NULL;
+ printf(
+ "free %d %d(%d): ok\n",
+ process,
+ old_size,
+ processes[process].size
+ );
+ int core_base = in_core ? processes[process].core_item.base : -1;
+ int core_size = in_core ? processes[process].core_item.limit - core_base : 0;
+ int swap_base = in_swap ? processes[process].swap_item.base : -1;
+ int swap_size = in_swap ? processes[process].swap_item.limit - swap_base : 0;
+ printf("old core [%d,%d) swap [%d,%d)\n", core_base, core_base + core_size, swap_base, swap_base + swap_size);
+ for (int i = 0; i < processes[process].size; ++i) {
+ long long hash = process * 17 + i * 29;
+ hash = (hash & 0xffffffffffffffffLL) + (hash >> 32);
+ hash = (hash & 0xffffffffffffffffLL) + (hash >> 32);
+ if (i < core_size) {
+ rassert(core_mem[core_base + i] == (int)hash);
+ core_mem[core_base + i] = 0xaaaaaaaa;
+ }
+ else {
+ rassert(swap_mem[swap_base + processes[process].size + ~i] == (int)hash);
+ swap_mem[swap_base + processes[process].size + ~i] = 0xaaaaaaaa;
+ }
+ }
+ }
+ }
+ else
+ rassert(false);
+ }
+
+done:
+ printf("final state:\n");
+ for (int i = 0; i < n_processes; ++i) {
+ if (processes[i].item.prev == NULL)
+ printf("process %d: not allocated\n", i);
+ else {
+ bool in_core = processes[i].core_item.prev != NULL;
+ int core_base = in_core ? processes[i].core_item.base : -1;
+ int core_size = in_core ? processes[i].core_item.limit - core_base : 0;
+ bool in_swap = processes[i].swap_item.prev != NULL;
+ int swap_base = in_swap ? processes[i].swap_item.base : -1;
+ int swap_size = in_swap ? processes[i].swap_item.limit - swap_base : 0;
+ printf("process %d: core [%d,%d) swap [%d,%d)\n", i, core_base, core_base + core_size, swap_base, swap_base + swap_size);
+ for (int j = 0; j < processes[i].size; ++j) {
+ long long hash = i * 17 + j * 29;
+ hash = (hash & 0xffffffffffffffffLL) + (hash >> 32);
+ hash = (hash & 0xffffffffffffffffLL) + (hash >> 32);
+ if (j < core_size) {
+ rassert(core_mem[core_base + j] == (int)hash);
+ core_mem[core_base + j] = 0xaaaaaaaa;
+ }
+ else {
+ rassert(swap_mem[swap_base + processes[i].size + ~j] == (int)hash);
+ swap_mem[swap_base + processes[i].size + ~j] = 0xaaaaaaaa;
+ }
+ }
+ }
+ }
+
+ for (int i = 0; i < core_size; ++i)
+ rassert(core_mem[i] == 0xaaaaaaaa);
+ for (int i = 0; i < swap_size; ++i)
+ rassert(swap_mem[i] == 0xaaaaaaaa);
+
+ return 0;
+}
--- /dev/null
+#ifndef _RASSERT_H
+#define _RASSERT_H 1
+
+#include <assert.h>
+
+# define rassert(expr) \
+ ((void) sizeof ((expr) ? 1 : 0), __extension__ ({ \
+ if (expr) \
+ ; /* empty */ \
+ else \
+ __assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION); \
+ }))
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "core.h"
+#include "swap.h"
+#include "rassert.h"
+
+struct pool_head swap_head;
+int *swap_mem;
+
+static void swap_move(struct pool_item *item, int new_base) {
+ int base = item->base;
+ int size = item->limit - base;
+ printf(
+ "swap_move [%d,%d) to [%d,%d)\n",
+ base,
+ base + size,
+ new_base,
+ new_base + size
+ );
+ assert(new_base < base || new_base >= base + size);
+ for (int i = 0; i < size; ++i) {
+ swap_mem[new_base + i] = swap_mem[base + i];
+ swap_mem[base + i] = 0xaaaaaaaa;
+ }
+}
+
+static void swap_move_up(struct pool_item *item, int new_limit) {
+ int limit = item->limit;
+ assert(limit != new_limit);
+ int neg_size = item->base - limit;
+ printf(
+ "swap_move_up [%d,%d) to [%d,%d)\n",
+ limit + neg_size,
+ limit,
+ new_limit + neg_size,
+ new_limit
+ );
+ assert(new_limit > limit || new_limit <= limit + neg_size);
+ for (int i = -1; i >= neg_size; --i) {
+ swap_mem[new_limit + i] = swap_mem[limit + i];
+ swap_mem[limit + i] = 0xaaaaaaaa;
+ }
+}
+
+void swap_init(int swap, int spare) {
+ pool_init(&swap_head, 0, swap, spare, swap_move, swap_move_up);
+
+ swap_mem = malloc(swap * sizeof(int));
+ memset(swap_mem, 0xaa, swap * sizeof(int));
+}
+
+// read forward from swap_base and write backward from core_limit
+void swap_pop(int swap_limit, int core_base, int size) {
+ printf("swap_pop swap [%d,%d) to core [%d,%d)\n", swap_limit - size, swap_limit, core_base, core_base + size);
+ for (int i = 0; i < size; ++i) {
+ assert(core_mem[core_base + i] == 0xaaaaaaaa);
+ core_mem[core_base + i] = swap_mem[swap_limit + ~i];
+ swap_mem[swap_limit + ~i] = 0xaaaaaaaa;
+ }
+}
+
+void swap_push(int core_limit, int swap_base, int size) {
+ printf("swap_push core [%d,%d) to swap [%d,%d)\n", core_limit - size, core_limit, swap_base, swap_base + size);
+ for (int i = 0; i < size; ++i) {
+ assert(swap_mem[swap_base + i] == 0xaaaaaaaa);
+ swap_mem[swap_base + i] = core_mem[core_limit + ~i];
+ core_mem[core_limit + ~i] = 0xaaaaaaaa;
+ }
+}
--- /dev/null
+#ifndef _SWAP_H
+#define _SWAP_H 1
+
+#include "pool.h"
+
+extern struct pool_head swap_head;
+extern int *swap_mem;
+
+void swap_init(int swap, int spare);
+void swap_pop(int swap_limit, int core_base, int size);
+void swap_push(int core_limit, int swap_base, int size);
+
+#endif