Segmentation fault when calling enif_free() - c

I have some code that should manage a 3 dimensional array with O(1) access and reading time in Erlang. Therefor I'm using Erlang NIFs. Everything is working fine except for the release() function. I always get a segmentation fault when calling it and I have no idea why.
Here is my code:
#include "erl_nif.h"
static ErlNifResourceType *DATA_RESOURCE;
typedef struct
{
int size;
ERL_NIF_TERM *** array;
ERL_NIF_TERM defaultValue;
} DATA;
static ERL_NIF_TERM new3DimArray(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
DATA *data = (DATA *)enif_alloc_resource(DATA_RESOURCE, sizeof(DATA));
int size;
enif_get_int(env, argv[0], &size);
if(argc > 1)
{
data->defaultValue = argv[1];
}else{
data->defaultValue = NULL;
}
data->size = size;
data->array = (ERL_NIF_TERM ***)enif_alloc(sizeof(ERL_NIF_TERM **) * size);
int x = 0;
while(x < size)
{
data->array[x] = (ERL_NIF_TERM **)enif_alloc(sizeof(ERL_NIF_TERM *) * size);
int y = 0;
while(y < size)
{
data->array[x][y] = (ERL_NIF_TERM *)enif_alloc(sizeof(ERL_NIF_TERM) * size);
y++;
}
x++;
}
return enif_make_resource(env, data);
}
static ERL_NIF_TERM get_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
DATA *data;
enif_get_resource(env, argv[0], DATA_RESOURCE, &data);
int x;
int y;
int z;
enif_get_int(env, argv[1], &x);
enif_get_int(env, argv[2], &y);
enif_get_int(env, argv[3], &z);
ERL_NIF_TERM res = data->array[x][y][z];
if(res == NULL && data->defaultValue != NULL)
{
res = data->defaultValue;
}
return res;
}
static void set_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
DATA *data;
enif_get_resource(env, argv[0], DATA_RESOURCE, &data);
int x;
int y;
int z;
enif_get_int(env, argv[1], &x);
enif_get_int(env, argv[2], &y);
enif_get_int(env, argv[3], &z);
ERL_NIF_TERM value = argv[4];
data->array[x][y][z] = value;
}
static void release(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
DATA *data;
enif_get_resource(env, argv[0], DATA_RESOURCE, &data);
int x = 0;
while(x < data->size)
{
int y = 0;
while(y < data->size)
{
enif_free(data->array[x][y]);
y++;
}
enif_free(data->array[x]);
x++;
}
enif_free(data->array);
enif_release_resource(data);
}
static void cleanup(ErlNifEnv *env, void *obj){}
static int load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info){
DATA_RESOURCE = enif_open_resource_type(env, "mutArray", "DATA_RESOURCE", &cleanup, ERL_NIF_RT_CREATE, 0);
return 0;
}
static ErlNifFunc nif_funcs[] = {
{"new_3_dim_array", 1, new3DimArray},
{"new_3_dim_array", 2, new3DimArray},
{"get", 4, get_nif},
{"set", 5, set_nif},
{"release", 1, release}
};
ERL_NIF_INIT(mutArray, nif_funcs, load, NULL, NULL, NULL);
This is my Erlang code (to make the arity clearer):
module(mutArray).
%% ====================================================================
%% API functions
%% ====================================================================
-export([init/0, new_3_dim_array/1, new_3_dim_array/2, get/4, set/5, release/1]).
init() ->
erlang:load_nif("./mutArray", 0).
new_3_dim_array(_Size) ->
"NIF not loaded yet.".
new_3_dim_array(_Size, _DefaultValue) ->
"NIF not loaded yet.".
get(_Array, _X, _Y, _Z) ->
"NIF not loaded yet.".
set(_Array, _X, _Y, _Z, _Value) ->
"NIF not loaded yet.".
release(_Array) ->
"NIF not loaded yet.".
Btw, this is my Testcode:
mutArray:init(),
A = mutArray:new_3_dim_array(100),
mutArray:release(A).
EDIT: Ok it gets more and more weird... After some testing I fidured out that I get ** exception error: [] if enif_free(data->array); is the last call of the function. At every other position I still get the segmentation fault, even if there is just a println() after enif_free(data->array);. After some debugging I also figured out that every line before enif_free(data->array); was called. So the exception seems to happen at enif_free(data->array). Does anybody know what this means?
EDIT2: Simply leaving enif_free(data->array); out doesn't help either. I get a segmentation fault then as well.

I was able to get your code running correctly by fixing several problems.
First, your code assumes that it's OK to check the validity of an ERL_NIF_TERM by comparing it to NULL, which is incorrect. You can fix this by either initializing all of your array elements to 0 (by calling enif_make_int(env, 0) to set each element), or by using an array of structs where each struct holds an ERL_NIF_TERM and an unsigned char flag to indicate whether the term is valid or not. If you choose the latter approach, you could simply memset the struct values to 0, and if a caller requests an uninitialized element via mutArray:get/4, just return enif_make_badarg(env) to indicate they passed bad arguments to the call.
Second, both your set_nif and release functions are declared to return void when they need to return ERL_NIF_TERM instead. To fix this you can correct their return types, and then return argv[4] from set_nif and enif_make_int(env, 0) from release.
Lastly, the second argument to your enif_open_resource_type call needs to be NULL rather than the "mutArray" value you're passing, as the erl_nif man page indicates.

Related

Not seeing same value of integer upon returning to main class

I am working off some skeleton code for a hash table implementation. In the main class, there is a portion which checks for the key in the table. It allows for duplicates so the it is expecting to return an array of a specified size, and if the entries exceed that size then it is called again with a larger array. My issue is with the "num_results" pointer which is declared just before.
int num_values = 1;
valType* values = malloc(1 * sizeof(valType));
int* num_results = NULL;
get(ht, key, values, num_values, num_results);
printf("num_results: %d\n", (*num_results));
if ((*num_results) > num_values) {
values = realloc(values, (*num_results) * sizeof(valType));
get(ht, 0, values, num_values, num_results);
}
for (int i = 0; i < (*num_results); i++) {
printf("value of %d is %d \n", i, values[i]);
}
free(values);
It is declared to null (presumably because if there are no results then the memory isn't wasted?)
int get(hashtable* ht, keyType key, valType *values, int num_values, int* num_results) {
int slot = key % sizeof(ht);
struct node *entry = ht->entries[slot];
if(entry == NULL){
printf("There are no matching hashed keys");
return -1;
}
// Allocate the num_results, as just a NULL pointer was passed
if((num_results = malloc(sizeof(int))) == NULL){
return -1;
}
// Start it at 0 so that it cxan be incremented as we check
(*num_results) = 0;
printf("num_results: %d\n", (*num_results));
int temp = num_values;
while(entry != NULL){
if(entry->key == key){
++(*num_results);
if(temp != 0){
values[num_values-temp] = entry->value;
--temp;
}
}
entry = entry->next;
}
printf("num_results: %d\n", (*num_results));
return 0;
}
This is the get function, and as you can see I allocate the memory needed, set it to 0, and it increments as expected. The output looks like:
num_results: 0
num_results: 2
num_results: 73896
This confuses me, as clearly the 2 result is from the last line of the method, and the last printout comes immediately after returning to the main... What is happening here? Why is the value changing?
You have to pass the pointer num_results by reference. Otherwise the function deals with a copy of the pointer.
For example
int get(hashtable* ht, keyType key, valType *values, int num_values, int ** num_results) {
// ...
if(( *num_results = malloc(sizeof(int))) == NULL){
return -1;
}
//…
A function call will look like
get(ht, key, values, num_values, &num_results);
Actually I do not see a great sense to declare the variable num_results as a pointer and allocate dynamically a memory for it in the function. I would declare it at least as having the type unsigned int.
For example
unsigned int num_results = 0;
and then the function get could look like
int get(hashtable* ht, keyType key, valType *values, int num_values, unsigned int *num_results) {
//…
*num_results = 0;
//…
and called like
get(ht, key, values, num_values, &num_results);
Pay attention to that instead of
int slot = key % sizeof(ht);
it seems you mean
int slot = key % sizeof( *ht);

Proper resource handling in Elixir NIF

I am trying to implent a NIF for simple linear algebra. Here is my internal structure for matrix:
typedef struct la_matrix {
uint rows, columns;
double **data;
} la_matrix;
And here is a "constructor" for it:
la_result
la_matrix_constructor(la_matrix **res,
const uint rows,
const uint columns)
{
if (rows == 0 || columns == 0)
return dimensional_problems;
// allocate memory for meta-structure
*res = malloc(sizeof(la_matrix));
if (*res == NULL)
return null_ptr;
// allocater memory for array of pointers to rows
(*res)->data = malloc(rows * sizeof(double*));
if ((*res)->data == NULL) {
free(*res);
return null_ptr;
}
//allocate memory for each row
uint i = 0;
bool failed = false;
for (; i < rows; i++) {
(*res)->data[i] = malloc(columns * sizeof(double));
if ((*res)->data[i] == NULL) {
failed = true;
break;
}
}
if (failed) {
// one step back, since i-th row wasn't allocated
i -= 1;
for(; i < ~((uint) 0); i--)
free((*res)->data[i]);
free((*res)->data);
free(*res);
return null_ptr;
}
(*res)->rows = rows;
(*res)->columns = columns;
return ok;
}
Then I have two wrappers for NIF -- one for constructor:
static ERL_NIF_TERM
nif_matrix_constructor(ErlNifEnv *env,
int argc,
const ERL_NIF_TERM *argv)
{
uint rows, columns;
enif_get_uint(env, argv[0], &rows);
enif_get_uint(env, argv[1], &columns);
la_matrix **mat_res = enif_alloc_resource(LA_MATRIX_TYPE, sizeof(la_matrix *));
la_matrix *mat_ptr;
la_result result = la_matrix_constructor(&mat_ptr, rows, columns);
if (result != ok)
return enif_make_atom(env, "err");
memcpy((void *) mat_res, (void *) &mat_ptr, sizeof(la_matrix *));
ERL_NIF_TERM term = enif_make_resource(env, mat_res);
enif_release_resource(mat_res);
return term;
}
And one to test if the constructor works correctly:
static ERL_NIF_TERM
nif_matrix_rows(ErlNifEnv *env,
int argc,
const ERL_NIF_TERM *argv)
{
la_matrix *mat_ptr;
if(!enif_get_resource(env, argv[0], LA_MATRIX_TYPE, (void **) &mat_ptr))
return enif_make_atom(env, "err");
return enif_make_uint(env, mat_ptr->rows);
}
It seems that the constructor wrapper works perfectly fine (I've tested it with using printf), but nif_matrix_rows returns strange results, e.g.
iex(1)> mat = LinearAlgebra.matrix(2,3)
""
iex(2)> LinearAlgebra.rows(mat)
1677732752
And directly passing LinearAlgebra.matrix(2,3) to LinearAlgebra.rows twice results in segfault:
iex(3)> LinearAlgebra.rows(LinearAlgebra.matrix(2,3))
1543520864
iex(4)> LinearAlgebra.rows(LinearAlgebra.matrix(2,3))
zsh: (core dumped) iex -S mix
(Note different results for the "same" matrices).
I was following Andrea Leopardi's tutorial with minor (I don't really sure if they are so) changes to fight gcc warnings. Most important, IMHO, was this part
la_matrix *mat_ptr;
if(!enif_get_resource(env, argv[0], LA_MATRIX_TYPE, (void **) &mat_ptr))
return enif_make_atom(env, "err");
while Andrea Leopardi uses
db_conn_t **conn_res;
enif_get_resource(env, argv[0], DB_RES_TYPE, (void *) conn_res);
db_conn_t *conn = *conn_res;
But it looks invalid for me since, AFAIR, (void *) conn_res assumes that conn_res was initalized.
Here is an error which occurs when I use Andrea's way:
src/nif.c: In function ‘nif_matrix_rows’:
src/nif.c:72:3: warning: ‘mat_res’ is used uninitialized in this function [-Wuninitialized]
enif_get_resource(env, argv[0], LA_MATRIX_TYPE, (void *) mat_res);
And calling LinearAlgebra.rows from iex causes segfault.
Could one tell me a proper way for handling structures in NIFs?
P.S. Sorry for C code, I've never wrote something more than bunch of helloworlds.
The problem was indeed in nif_matrix_rows: with my code Elixir passes a pointer to a pointer to a structure (la_matrix **) and I assumed that it would be a proper pointer.
So, quick fix is
static ERL_NIF_TERM
nif_matrix_rows(ErlNifEnv *env,
int argc,
const ERL_NIF_TERM *argv)
{
la_matrix const **mat_res;
if(!enif_get_resource(env, argv[0], LA_MATRIX_TYPE,(void **) &mat_res))
return enif_make_atom(env, "err");
la_matrix const *mat_ptr = *mat_res;
return enif_make_uint(env, mat_ptr->rows);
}
However, I'll wait some time for more elegant solution and won't accept this answer so far.

Loss of values in array in struct after function execution

I am working on a c code that holds a structure that hosts some values which I call range.
My purpose is to use this so called range dynamically (holding different amount of data at every execution). I am now provisionally using the # define comp instead. This so called range gets updated every time I call my update_range though the use of s1 structure (and memory allocations).
What I found weird is that when I introduced a "show_range" function to output the actual values inside/outside the update function I realized that I loose the first two values.
Here is the code.
Any suggestions on that?
Thanks in advance!
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <errno.h>
#include <string.h>
#include <complex.h>
#define comp 1024
// struct holding a complex-valued range
struct range {
int dimensions; /* number of dimensions */
int* size; /* array holding number of points per dimension */
complex double* values; /* array holding complex valued */
int components; /* number of components that will change on any execution*/
};
// parameters to use in function
struct s1 {
int tag;
struct range* range;
};
int update_range(struct s1* arg);
int show_range(struct range* argrange, char* message);
int copy_range(struct range* in, struct range* out);
int main(void) {
int ret = 0;
struct s1 s1;
s1.tag = 0;
s1.range = malloc(sizeof(struct range));
update_range(&s1);
show_range(s1.range, "s1.range inside main function");
return ret;
}
////////////////////////////////////////////
int update_range(struct s1* arg) {
int ret = 0;
int i;
struct range range;
range.dimensions = 1;
range.size = malloc(range.dimensions * sizeof(int));
range.components = comp;
range.size[0] = range.components; // unidimensional case
range.values = malloc(range.components * sizeof(complex double));
for (i = 0; i < range.components; i++) {
range.values[i] = (i + 1) + I * (i + 1);
}
show_range(&range, "range inside update_range function");
arg->range->size =
malloc(range.dimensions * sizeof(int)); // size was unknown before
arg->range->values =
malloc(comp * sizeof(complex double)); // amount of values was unknown
copy_range(&range, arg->range);
show_range(arg->range, "arg->range inside update_range function");
if (range.size)
free(range.size);
range.size = NULL;
if (range.values)
free(range.values);
range.values = NULL;
return ret;
}
////////////////////////////////////////////
// Show parameters (10 first values)
int show_range(struct range* argrange, char* message) {
int ret = 0;
vint i;
printf(" ******************************\n");
printf(" range in %s \n", message);
printf(" arg.dimensions=%d \n", argrange->dimensions);
printf(" arg.size[0]=%d \n", argrange->size[0]);
printf(" argrange.components=%d \n", argrange->components);
printf(" first 10 {Re} values: \n");
for (i = 0; i < 10; i++) {
printf(" argrange.values[%d]=%f\n", i, creal(argrange->values[i]));
}
printf("\n");
return ret;
}
////////////////////////////////////////////
// copy range
int copy_range(struct range* in, struct range* out) {
int ret = 0;
if (in == NULL) {
fprintf(stderr, "error: in points to NULL (%s:%d)\n", __FILE__,
__LINE__);
ret = -1;
goto cleanup;
}
if (out == NULL) {
fprintf(stderr, "error: out points to NULL (%s:%d)\n", __FILE__,
__LINE__);
ret = -1;
goto cleanup;
}
out->dimensions = in->dimensions;
out->size = in->size;
out->values = in->values;
out->components = in->components;
cleanup:
return ret;
}
Your copy_range function is broken, because it copy only pointer to size and values and not the memory. After you call free(range.size); and free(range.values); you are deleting mamory also from original object but without setting its pointers back to NULL.
After calling update_range, s1.range has non NULL pointers in size and values, but they are pointing to deleted memory.
You are experiencing undefined behaviour (UB) due to accessing freed memory. Your copy_range() function only does a shallow copy of the two pointer fields so when you run free(range->size) you make arg->range->size invalid.
You should make copy_range() a deep copy by allocating and copying the pointer contents like:
out->size = malloc(in->dimensions * sizeof(int));
memcpy(out->size, in->size, in->dimensions * sizeof(int));
out->values = malloc(in->components * sizeof(complex double));
memcpy(out->values , in->values, in->components * sizeof(complex double));
There are not 10 items to print, so the lines:
printf(" first 10 {Re} values: \n");
for (i = 0; i < 10; i++) {
printf(" argrange.values[%d]=%f\n", i, creal(argrange->values[i]));
}
Will be printing from random memory.
a much better method would be:
printf(" first %d {Re} values: \n", min(argrange.components,10));
for (i = 0; i < argrange.components; i++) {
printf(" argrange.values[%d]=%f\n", i, creal(argrange->values[i]));
}
The above is just one of many problems with the code.
I would suggest executing the code using a debugger to get the full story.
as it is, the code has some massive memory leaks due mostly
to overlaying malloc'd memory pointers.
for instance as in the following:
arg->range->size =
malloc(range.dimensions * sizeof(int)); // size was unknown before
arg->range->values =
malloc(comp * sizeof(complex double)); // amount of values was unknown

How do I initialize a Priority Queue (custom made library)

So for this project, we are required to check if words are palindromes by looking down a stack and forwards through a queue. I have all the logic together, as well as the #include in my palindrome.c file. All I'm having trouble with is defining it correctly while using (allocPQueue seen below) in my palindrome checking method.
Here is an excerpt the .c for PriorityQueue
#include "PriorityQueue.h"
PQueue* allocPQueue(uint elementSize, PQMode mode){
PQueue* pq = (PQueue*)calloc(1, sizeof(PQueue));
pq->elements = allocDList(elementSize, NULL, NULL);
//the elementSampling is to speed up search time, but it is not yet ready
//in order for it to work I cannot make a copy of DNode*, rather I should
//place the pointer of a DNode directly into the array
//I will call this a DeepDArray
pq->elementSampling = (GenericArray)allocDArray(10, sizeof(DNode*));
pq->mode = mode;
if(mode == PQMODE_STACK){
pq->priorityExtractor = &stackPriority;
}
else if(mode == PQMODE_QUEUE){
pq->priorityExtractor = &queuePriority;
}
return pq;
}
void releasePQueue(PQueue* pq){
if(pq){
if(pq->elements){
releaseDList(pq->elements);
}
if(pq->elementSampling){
releaseDArray(pq->elementSampling);
}
free(pq);
}
}
Object peekMin(PQueue* pq){
if(isEmptyPQueue(pq)){
return NULL;
}
Object data = malloc(pq->elements->elementSize);
memcpy(data, pq->elements->head->data, pq->elements->elementSize);
return data;
}
Here is the code in my palindrome.c file (excluding inclusions):
#include "PriorityQueue.h"
bool isPalindrome(char* str, uint length);
char getPalindromeChar(char c);
PQueue* stack;
PQueue* queue;
int main(int argc, char** argv){
if(isPalindrome(argv[1], strlen(argv[1]))){
printf("%s is a palindrome\n", argv[1]);
}
else{
printf("%s is not a palindrome\n", argv[1]);
}
return 0;
}
bool isPalindrome(char* str, uint length){
//TODO
//insert your check, you are only allowed to use a Stack, a Queue, and the char
//getPalindromeChar(char) helper function
char ch;
int wordLength;
int counter;
char stackChar;
char queueChar;
bool stillPalli;
stack = allocPQueue(sizeof(char), PQMODE_STACK); <--"undefined reference to 'allocPQueue'"
queue = allocPQueue(sizeof(char), PQMODE_QUEUE); <--"undefined reference to 'allocPQueue'"
wordLength = 0;
int i;
for(i = 0; i < length; i++){ // Goes through the str array, looking char-by-char for
ch = str[i];
ch = getPalindromeChar(ch);
wordLength++;
// places them in stack and queue
add(stack,ch);
add(queue,ch);
}
stillPalli = true;
while(stillPalli && (counter < wordLength)){
stackChar = top(stack);
pop(stack);
queueChar = front(queue);
dequeue(queue);
// test for equality
if(strcmp(stackChar, queueChar) != 0){
stillPalli = false;
}
counter++;
}
}
Declare your function allocPQueue() inside PriorityQueue.h:
extern PQueue* allocPQueue(uint elementSize, PQMode mode);
When compiling your palindrome.c the compiler needs to know that there is, somewhere, a function that will be available at link time.
The extern keyword does exactly this: it populates the compiler symbol table but don't try to define the actual contents (the assembly code, in the case of a function) of it. The compiler will output the object code (palindrome.o) with annotations that this function still lacks a definition. It will come from another object file.
Then enters the linker. You should pass both palindrome.o and PriorityQueue.o, so that no reference goes undefined on the final executable file.

Producer/consumer with bounded buffer

Could someone check my code and tell me if I am on the right track.. It seems like I am a bit lost.. if you see my errors, please let me know them..
What I am trying to do is to solve bounded buffer using my own semaphores as well as GCD.
Thanks in advance..
sema.c
void procure( Semaphore *semaphore ) {
pthread_mutex_lock(semaphore->mutex1);
while(semaphore->value <= 0)
pthread_cond_wait(&semaphore->condition, semaphore->mutex1);
semaphore->value--;
pthread_mutex_unlock(semaphore->mutex1);
}
void vacate( Semaphore *semaphore ) {
pthread_mutex_lock(semaphore->mutex1);
semaphore->value++;
pthread_cond_signal(&semaphore->condition);
pthread_mutex_unlock(semaphore->mutex1);
}
void init ( Semaphore *semaphore ){
semaphore->value = 1;
pthread_mutex_t myMutex;
semaphore->mutex1 = &myMutex;
pthread_mutex_init( semaphore->mutex1, NULL);
}
void destroy ( Semaphore *semaphore ) {
pthread_mutex_destroy(semaphore->mutex1);
}
and main.c
struct variables {
Semaphore *sem;
};
struct variables vars;
void constructer (int *buffer, int *in, int *out) {
init(vars.sem);
}
void deconstructer () {
destroy(vars.sem);
}
int rand_num_gen() {
uint_fast16_t buffer;
int file;
int *rand;
file = open("/dev/random", O_RDONLY);
while( 1 ) {
read(file, &buffer, sizeof(buffer));
printf("16 bit number: %hu\n", buffer );
*rand = (int) buffer;
close(file);
break;
}
return *rand;
}
void put_buffer( int* buffer, int* in, int* out ) {
buffer[*in] = rand_num_gen(); // produce
procure(vars.sem); // wait here
*in = (*in + 1) % BUF_SIZE;
vacate(vars.sem);
}
void get_buffer( int* buffer, int* in, int* out ) {
int value;
procure(vars.sem);
value = buffer[*out];
vacate(vars.sem);
*out = (*out + 1) % BUF_SIZE;
}
int main (void) {
int *in, *out, *buffer;
constructer(buffer, in, out);
dispatch_queue_t producer, consumer;
producer = dispatch_queue_create("put_buffer", NULL);
consumer = dispatch_queue_create("get_buffer", NULL);
dispatch_async(producer,
^{
int i;
do
{
put_buffer( buffer, in, out );
dispatch_async(consumer,
^{
get_buffer( buffer, in, out );
if (i == RUN_LENGTH) exit(EXIT_SUCCESS);
});
}
while (i < RUN_LENGTH);
});
dispatch_main();
deconstructer();
exit (0);
}
Your code has a bug. In the init function you assign the address of a local variable to semaphore->mutex1, and when the function returns this address will be invalid. Later you still use this address, so this leads to undefined behavior.
You must either allocate the memory for the mutex directly in the semaphore (without a pointer) or allocate the memory via malloc.
Update:
Your program has so many bugs that you should definitely pick an easier topic to learn the basic concepts about memory management, how to allocate, use and reference a buffer, do proper error handling, etc. Here is a slightly edited version of your code. It still won't work, but probably has some ideas that you should follow.
#include <limits.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
void procure(Semaphore *semaphore) {
pthread_mutex_lock(semaphore->mutex1);
while (semaphore->value <= 0)
pthread_cond_wait(&semaphore->condition, semaphore->mutex1);
semaphore->value--;
pthread_mutex_unlock(semaphore->mutex1);
}
void vacate(Semaphore *semaphore) {
pthread_mutex_lock(semaphore->mutex1);
semaphore->value++;
pthread_cond_signal(&semaphore->condition);
pthread_mutex_unlock(semaphore->mutex1);
}
struct variables {
mutex_t sem_mutex;
Semaphore sem;
};
struct variables vars;
void constructor(int *buffer, int *in, int *out) {
vars.sem.value = 1;
vars.sem.mutex1 = &vars.sem_mutex;
pthread_mutex_init(vars.sem.mutex1, NULL);
}
void deconstructor() {
pthread_mutex_destroy(&semaphore->mutex1);
}
int rand_num_gen() {
const char *randomfile = "/dev/random";
unsigned char buffer[2]; // Changed: always treat files as byte sequences.
FILE *f = fopen(randomfile, "rb");
// Changed: using stdio instead of raw POSIX file access,
// since the API is much simpler; you don't have to care
// about interrupting signals or partial reads.
if (f == NULL) { // Added: error handling
fprintf(stderr, "E: cannot open %s\n", randomfile);
exit(EXIT_FAILURE);
}
if (fread(buffer, 1, 2, f) != 2) { // Added: error handling
fprintf(stderr, "E: cannot read from %s\n", randomfile);
exit(EXIT_FAILURE);
}
fclose(f);
int number = (buffer[0] << CHAR_BIT) | buffer[1];
// Changed: be independent of the endianness of the system.
// This doesn't matter for random number generators but is
// still an important coding style.
printf("DEBUG: random number: %x\n", (unsigned int) number);
return number;
}
void put_buffer( int* buffer, int* in, int* out ) {
buffer[*in] = rand_num_gen(); // produce
procure(&vars.sem); // wait here
*in = (*in + 1) % BUF_SIZE;
vacate(&vars.sem);
}
void get_buffer( int* buffer, int* in, int* out ) {
int value;
procure(&vars.sem);
value = buffer[*out];
vacate(&vars.sem);
*out = (*out + 1) % BUF_SIZE;
}
int main (void) {
int inindex = 0, outindex = 0;
int buffer[BUF_SIZE];
constructor(buffer, &inindex, &outindex);
// Changed: provided an actual buffer and actual variables
// for the indices into the buffer.
dispatch_queue_t producer, consumer;
producer = dispatch_queue_create("put_buffer", NULL);
consumer = dispatch_queue_create("get_buffer", NULL);
dispatch_async(producer, ^{
int i;
do {
put_buffer(buffer, &inindex, &outindex);
dispatch_async(consumer, ^{
get_buffer(buffer, &inindex, &outindex);
if (i == RUN_LENGTH) exit(EXIT_SUCCESS);
});
} while (i < RUN_LENGTH);
});
dispatch_main();
deconstructor();
exit (0);
}
As I said, I didn't catch all the bugs.

Resources