354 lines
10 KiB
C
354 lines
10 KiB
C
/*---
|
|
This software is copyrighted by the Regents of the University of
|
|
California, and other parties. The following terms apply to all files
|
|
associated with the software unless explicitly disclaimed in
|
|
individual files.
|
|
|
|
The authors hereby grant permission to use, copy, modify, distribute,
|
|
and license this software and its documentation for any purpose,
|
|
provided that existing copyright notices are retained in all copies
|
|
and that this notice is included verbatim in any distributions. No
|
|
written agreement, license, or royalty fee is required for any of the
|
|
authorized uses. Modifications to this software may be copyrighted by
|
|
their authors and need not follow the licensing terms described here,
|
|
provided that the new terms are clearly indicated on the first page of
|
|
each file where they apply.
|
|
|
|
IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
|
|
FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
|
ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
|
|
DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
|
|
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
|
|
NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND
|
|
THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
|
|
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
|
|
|
GOVERNMENT USE: If you are acquiring this software on behalf of the
|
|
U.S. government, the Government shall have only "Restricted Rights" in
|
|
the software and related documentation as defined in the Federal
|
|
Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you are
|
|
acquiring the software on behalf of the Department of Defense, the
|
|
software shall be classified as "Commercial Computer Software" and the
|
|
Government shall have only "Restricted Rights" as defined in Clause
|
|
252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the
|
|
authors grant the U.S. Government and others acting in its behalf
|
|
permission to use and distribute the software in accordance with the
|
|
terms specified in this license.
|
|
---*/
|
|
#include "monotree.h"
|
|
#include <limits.h>
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#define EPOCH ((time_t)0)
|
|
|
|
int getMachineIndex(MonoTree * rb, state_machine_id id);
|
|
void compactBuffer(MonoTree * rb);
|
|
/**
|
|
Called when a new MonoTree is initialized (NOT after recovery.)
|
|
|
|
This code takes a pointer to a MonoTree as an argument, since the
|
|
MonoTree should be allocated elsewhere (by the transactional
|
|
layer).
|
|
*/
|
|
|
|
void initStateMachine(StateMachine * stateMachine) {
|
|
|
|
stateMachine->machine_id = ULONG_MAX;
|
|
stateMachine->last_transition = EPOCH;
|
|
stateMachine->page = NULL;
|
|
/* stateMachine->page_id; */
|
|
stateMachine->current_state = NULL_STATE;
|
|
stateMachine->pending = 0;
|
|
|
|
/* Intentionally does not free mutexes. */
|
|
}
|
|
|
|
void init_MonoTree(MonoTree * rb, int size) {
|
|
int i;
|
|
|
|
for(i = 0; i < size; i++) {
|
|
StateMachine * stateMachine = &(rb->buffer[i]);
|
|
initStateMachine(stateMachine);
|
|
|
|
|
|
}
|
|
rb->size = size;
|
|
rb->low_water_mark = 0;
|
|
rb->high_water_mark = 0;
|
|
rb->next_id = 0;
|
|
}
|
|
|
|
StateMachine * allocMachine(MonoTree * rb/*, state_machine_id id*/) {
|
|
|
|
StateMachine * new;
|
|
|
|
if(rb->high_water_mark >= rb->size) {
|
|
compactBuffer(rb);
|
|
}
|
|
if(rb->high_water_mark >= rb->size) {
|
|
return (StateMachine *)0;
|
|
}
|
|
new = &(rb->buffer[rb->high_water_mark]);
|
|
rb->high_water_mark++;
|
|
new->machine_id = rb->next_id;
|
|
new->mutex = malloc(sizeof(pthread_mutex_t));
|
|
new->sleepCond = malloc(sizeof(pthread_cond_t));
|
|
|
|
pthread_mutex_init(new->mutex, NULL);
|
|
pthread_cond_init(new->sleepCond, NULL);
|
|
|
|
rb->next_id++;
|
|
new->current_state = START_STATE;
|
|
|
|
return new;
|
|
}
|
|
|
|
StateMachine * insertMachine(MonoTree * rb, state_machine_id id) {
|
|
int new_index;
|
|
StateMachine * new;
|
|
int insertion_point;
|
|
/* allocMachine is much less expensive than insertMachine, so this
|
|
check is probably worth the trouble
|
|
|
|
I don't understand why, but this optimization was causing 2pc to
|
|
fail, so I commented it out.
|
|
|
|
-Rusty.
|
|
*/
|
|
/* if(id == rb->next_id) {
|
|
return allocMachine(rb);
|
|
} */
|
|
if(rb->high_water_mark >= rb->size) {
|
|
compactBuffer(rb);
|
|
}
|
|
if(rb->high_water_mark >= rb->size) {
|
|
return (StateMachine *)0;
|
|
}
|
|
/* Look up the new machine */
|
|
|
|
new_index = getMachineIndex(rb, id);
|
|
|
|
if(new_index < 0) {
|
|
insertion_point = -(1+new_index);
|
|
memmove(&(rb->buffer[insertion_point+1]),
|
|
&(rb->buffer[insertion_point]),
|
|
sizeof(StateMachine) * (rb->high_water_mark - insertion_point));
|
|
rb->high_water_mark++;
|
|
} else {
|
|
if(rb->buffer[new_index].current_state == NULL_STATE) {
|
|
insertion_point = new_index;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
new = &(rb->buffer[insertion_point]);
|
|
new->machine_id = id;
|
|
new->current_state = START_STATE;
|
|
new->mutex = malloc(sizeof(pthread_mutex_t));
|
|
new->sleepCond = malloc(sizeof(pthread_cond_t));
|
|
pthread_mutex_init(new->mutex, NULL);
|
|
pthread_cond_init(new->sleepCond, NULL);
|
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
|
void freeMachine(MonoTree * rb, state_machine_id id) {
|
|
StateMachine * stateMachine;
|
|
int old_index = getMachineIndex(rb, id);
|
|
|
|
/* Needed for optimization to garbage collector. */
|
|
|
|
if (old_index < rb->low_water_mark) {
|
|
rb->low_water_mark = old_index;
|
|
}
|
|
|
|
stateMachine = &(rb->buffer[old_index]);
|
|
|
|
/* If either of these fail, then there's a bug above this line,
|
|
or someone attempted to free a machine that doesn't exist (anymore?). */
|
|
|
|
assert(stateMachine->machine_id == id);
|
|
assert(stateMachine->current_state != NULL_STATE);
|
|
|
|
/* Leave the machine's id intact for now so that it can be used for binary search. */
|
|
stateMachine->current_state = NULL_STATE;
|
|
|
|
/* Needed so that alloc_machine can be correctly implemented. */
|
|
|
|
if ((old_index + 1) == rb->high_water_mark) {
|
|
rb->high_water_mark--;
|
|
/* Since we're at the end of the array, we can do this. */
|
|
stateMachine->machine_id = ULONG_MAX;
|
|
}
|
|
pthread_mutex_destroy(stateMachine->mutex);
|
|
pthread_cond_destroy(stateMachine->sleepCond);
|
|
free(stateMachine->mutex);
|
|
free(stateMachine->sleepCond);
|
|
|
|
|
|
/* The application is responsible for the memory management for page, so don't touch that either. */
|
|
}
|
|
|
|
StateMachine * getMachine(MonoTree * rb, state_machine_id id) {
|
|
int index = getMachineIndex(rb, id);
|
|
StateMachine * stateMachine;
|
|
if(index < 0) {
|
|
return 0;
|
|
}
|
|
stateMachine = &(rb->buffer[index]);
|
|
if (stateMachine->current_state == NULL_STATE) {
|
|
return 0;
|
|
}
|
|
assert(stateMachine->machine_id==id);
|
|
|
|
return stateMachine;
|
|
}
|
|
|
|
StateMachine * enumerateMachines(MonoTree * rb, int* count) {
|
|
compactBuffer(rb);
|
|
|
|
*count = rb->high_water_mark;
|
|
|
|
return rb->buffer;
|
|
}
|
|
|
|
/*----------- PRIVATE FUNCTIONS ----------*/
|
|
|
|
/* Return the highest possible machine id if binary search
|
|
* falls off the end of the array.
|
|
*/
|
|
state_machine_id getMachineID(MonoTree * rb, int index) {
|
|
if(index >= rb->size) {
|
|
return ULONG_MAX;
|
|
} else {
|
|
return rb->buffer[index].machine_id;
|
|
}
|
|
}
|
|
|
|
int __round_size_up_last_in = -1;
|
|
int __round_size_up_last_out = -1;
|
|
|
|
/* Find the smallest power of 2 greater than orig_size. */
|
|
int round_size_up(int orig_size) {
|
|
int power_of_two = 0;
|
|
|
|
int size = orig_size-1;
|
|
|
|
if(orig_size == __round_size_up_last_in) {
|
|
return __round_size_up_last_out;
|
|
}
|
|
|
|
|
|
assert(orig_size >= 0);
|
|
|
|
if(orig_size == 0) {
|
|
return 0;
|
|
}
|
|
|
|
/* What is the first power of two > size? */
|
|
while(size != 0) {
|
|
size /= 2;
|
|
power_of_two++;
|
|
}
|
|
|
|
size = 1 << power_of_two;
|
|
__round_size_up_last_in = orig_size;
|
|
__round_size_up_last_out = size;
|
|
|
|
return (int) size;
|
|
|
|
}
|
|
|
|
/**
|
|
Thanks to the spec of Java's Arrays.binarySearch() for this one. ;)
|
|
|
|
This code doesn't check to see if the index that it returns is the
|
|
correct one, or even that it contains a real machine, or even that
|
|
it's less than the size of rb's buffer.
|
|
|
|
*/
|
|
|
|
int getMachineIndex(MonoTree * rb, state_machine_id id) {
|
|
|
|
int size = round_size_up(rb->size);
|
|
|
|
int start = 0;
|
|
int stop = size;
|
|
int index;
|
|
state_machine_id index_id;
|
|
|
|
while(stop - start > 1) {
|
|
assert(((start+stop) % 2) == 0);
|
|
index = (start + stop) / 2;
|
|
index_id = getMachineID(rb, index);
|
|
|
|
if(index_id == id) {
|
|
start = index;
|
|
break;
|
|
} else if(index_id < id) {
|
|
start = index;
|
|
} else {
|
|
stop = index;
|
|
}
|
|
|
|
|
|
}
|
|
if(id == getMachineID(rb, start)) {
|
|
return start;
|
|
} else if(id == getMachineID(rb, stop)) {
|
|
return stop;
|
|
} else {
|
|
int insertionPoint;
|
|
int startID = getMachineID(rb,start);
|
|
int stopID = getMachineID(rb,stop);
|
|
if(id < startID) {
|
|
insertionPoint = start;
|
|
assert(start == 0 || getMachineID(rb, start-1) < id);
|
|
} else if(id < stopID) {
|
|
insertionPoint = stop;
|
|
} else {
|
|
assert(id < getMachineID(rb, stop+1));
|
|
insertionPoint = stop+1;
|
|
}
|
|
return (-(insertionPoint) - 1);
|
|
}
|
|
|
|
}
|
|
|
|
void compactBuffer(MonoTree * rb) {
|
|
|
|
int i;
|
|
int buffer_pos = 0;
|
|
int new_buffer_count = rb->high_water_mark-rb->low_water_mark;
|
|
size_t new_buffer_size = sizeof(StateMachine)*new_buffer_count;
|
|
StateMachine * new_buffer;
|
|
int number_of_machines = rb->low_water_mark;
|
|
if(rb->high_water_mark == rb->low_water_mark) {
|
|
return;
|
|
}
|
|
|
|
new_buffer = malloc(new_buffer_size);
|
|
for(i = rb->low_water_mark; i < rb->high_water_mark; i++) {
|
|
if(rb->buffer[i].current_state != NULL_STATE) {
|
|
memcpy(&(new_buffer[buffer_pos]), &(rb->buffer[i]), sizeof(StateMachine));
|
|
buffer_pos++;
|
|
number_of_machines++;
|
|
}
|
|
}
|
|
for(i = buffer_pos; i < new_buffer_count; i++) {
|
|
initStateMachine(&(new_buffer[i]));
|
|
}
|
|
memcpy(&(rb->buffer[rb->low_water_mark]), new_buffer, new_buffer_size);
|
|
free(new_buffer);
|
|
rb->low_water_mark = rb->high_water_mark = number_of_machines;
|
|
return;
|
|
}
|