I'm building a user defined shell. I have an array of pointers to functions -- that is, an array full of shared library functions that can be invoked at any point.
I typedef here
typedef void (*func_ptr)(char **);
func_ptr function;
void *pointers_to_functions[64];
I have debugged and confirmed that my initializations of placing the pointers into the array is working properly, but here's the code for safe measures...
void initialize_built_in(){
void *handle;
char *error;
int i;
for (i = 0; i < 5; i++){
handle = dlopen(builtin_files[i], RTLD_LOCAL | RTLD_LAZY);
if (!handle) {
fprintf(stderr, "%s\n", dlerror());
exit(1);
}
pointers_to_functions[i] = dlsym(handle, builtin_functions[i]);
if ((error = dlerror()) != NULL) {
fprintf(stderr, "%c\n", *error);
exit(1);
}
if (dlclose(handle) < 0) {
fprintf(stderr, "%c\n", *dlerror());
exit(1);
}
}
}
Here's where the seg fault occurs -- when I invoke the function
int execute_built_in(char **argv){
int i;
//scan through the builtin_functions strings to find the correct index of pointers_to_functions - that is, they have the same ordering
for (i = 0; i < sizeof(builtin_functions); i++){
if (!strcmp(argv[0], builtin_functions[i])){
//the index has been found
function = pointers_to_functions[i];
function(argv); //execute function
return 0;
}
}
return -1;
}
My shared library does indeed take argv as a parameter -- so I don't believe this is the problem.
As I said, debugging I see that the array of pointers is filled with addresses. I suppose it could be an incorrect address somehow, but I'm at a brick wall here.
Any ideas?
So I tested the pointer to function call be defining my own function type (void *) in the same file.
func_ptr function;
void *testfunction(char **);
void *pointers_to_functions[64] = {testfunction};
where the function just prints something out to the shell
then I added a condition in the execute_function function to force execution...
for (i = 0; i < sizeof(builtin_functions); i++){
if (i == 0){
function = pointers_to_functions[1];
char *bleh[] = {"bleh"};
function(bleh);
}
if (!strcmp(argv[0], builtin_functions[i])){
//the index has been found
function = pointers_to_functions[i];
function(argv); //execute function
return 0;
}
}
and it works WOOHOO!
So I either have a problem with my dynamic linking, which I can't see. Or with my shared library -- which is unlikely because I've already built successful libraries that work with another shell code of the same project.
So, what's wrong with my dynamic linking?
Here's an example shared library
#include <stdio.h>
#include <unistd.h>
struct NewBuiltIn{
char *CommandName[64];
char *FunctionName[64];
char *AnalyzerName[64];
};
struct NewBuiltIn pluggin_method = {{"cd", "cd", ""}};
void cd(char *argv[]) {
if(chdir(argv[1]) < 0){
printf("There was an error in changing your directory. Please check the path name and retry.\n");
}
}
Your init function is wrong:
you perform a dlclose(handle) which causes all the lib you loaded to be unmapped from memory, and obviously, the functions you expect to call are flying away.
You must keep the lib mapped to memory until you completely finished using the function pointers you stored.
You can probably check this by observing that the address that segfaults actually lies in the library mapped segment (in /proc/your_app_pid/maps)
Related
I'm trying to use a "fixed memory scheme" and pre-allocate memory & reuse it via alloc, init, free fashion as many times as possible.
free() will called at shutdown only, but I want to test many iterations.
Although I call my alloc function bn_tree_alloc_node_space_heap() & init function bn_tree_init_node_heap(), I can only call free function bn_tree_free_node_space once.
Below is a complete reproducible snippet of my memory management, maint_test.c:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <float.h>
#define BN_TREE_HEAP_SIZE 100
/*variables internal*/
typedef struct bntree_internals;
/*bn_tree_node is single bntree_t leaf*/
typedef struct bn_tree_node {
struct bn_tree_node* left;
struct bn_tree_node* right;
float* dataset;
float distance_to_neighbor;
int visited;
int heap_index;
} bn_tree_node;
/*tree*/
typedef struct {
/*in order to keep track of the bn-tree root*/
bn_tree_node* _root;
/*pointer to internal variables struct*/
struct bntree_internals* _internals;
} bntree_t;
/*bn tree leaf nodes heap*/
bn_tree_node* node_processing_space = NULL;
/*leaf nodes*/
void bn_tree_alloc_node_space_heap(int max_dimensions);
bn_tree_node*
get_pre_allocated_bn_tree_node_heap();
void bn_tree_init_node_heap(bn_tree_node* nodes, int max_dimensions);
void bn_tree_free_node_space(bn_tree_node* nodes);
int main(int argc, char** argv) {
/*PROBLEM:called the alloc,init,free cycle several times, problem,
getting seg fault on 2nd call of free()*/
bn_tree_alloc_node_space_heap(3);
assert(get_pre_allocated_bn_tree_node_heap());
printf("alloc\n");
bn_tree_init_node_heap(node_processing_space, 3);
printf("init\n");
bn_tree_free_node_space(node_processing_space);
printf("free\n");
bn_tree_alloc_node_space_heap(3);
assert(get_pre_allocated_bn_tree_node_heap());
printf("alloc\n");
bn_tree_init_node_heap(node_processing_space, 3);
printf("init\n");
bn_tree_free_node_space(node_processing_space);
printf("free\n");
bn_tree_alloc_node_space_heap(3);
assert(get_pre_allocated_bn_tree_node_heap());
printf("alloc\n");
bn_tree_init_node_heap(node_processing_space, 3);
printf("init\n");
bn_tree_free_node_space(node_processing_space);
printf("free\n");
bn_tree_alloc_node_space_heap(3);
assert(get_pre_allocated_bn_tree_node_heap());
printf("alloc\n");
bn_tree_init_node_heap(node_processing_space, 3);
printf("init\n");
bn_tree_free_node_space(node_processing_space);
printf("free\n");
return (EXIT_SUCCESS);
}
void bn_tree_alloc_node_space_heap(int max_dimensions) {
if (NULL == node_processing_space) {
node_processing_space = (bn_tree_node*) calloc(BN_TREE_HEAP_SIZE, sizeof (bn_tree_node));
//TODO: bn_tree_set_k_dimensions (max_dimensions);
int i = 0;
for (; i < BN_TREE_HEAP_SIZE; i++) {
node_processing_space[i].dataset = (float*) calloc(max_dimensions, sizeof (float));
}
//bn_heap_tail_index = bn_heap_head_index = 0;
}
}
bn_tree_node* get_pre_allocated_bn_tree_node_heap() {
return node_processing_space;
}
void bn_tree_init_node_heap(bn_tree_node* nodes, int max_dimensions) {
int i = 0;
int c = 0;
for (; i < BN_TREE_HEAP_SIZE; i++) {
/*reset values */
if (NULL != nodes[i].dataset) {
c = 0;
for (; c < max_dimensions; c++) {
nodes[i].dataset[c] = FLT_MIN;
}
}
nodes[i].visited = 0;
nodes[i].distance_to_neighbor = FLT_MAX;
nodes[i].left = NULL;
nodes[i].right = NULL;
nodes[i].heap_index = -1;
}
}
/*PROBLEM is subsequent call to free(), but if I alloc again why cant I free again?*/
void bn_tree_free_node_space(bn_tree_node* nodes) {
int i = 0;
for (; i < BN_TREE_HEAP_SIZE; i++) {
if (nodes[i].dataset) {
free(nodes[i].dataset);
}
}
free(nodes);
nodes = NULL;
}
Here is the output that I expect/want:
alloc
init
free
alloc
init
free
alloc
init
free
alloc
init
free
But Im getting this output/error:
alloc
init
free
alloc
init
double free or corruption (!prev)
Aborted (core dumped)
How can fix this?
Can't I do alloc,init,free as many times as I want (as long as I called alloc before free) OR I can do only alloc() once, then many init(), free() once?
Thanks a million & please be kind enough to provide concise answers with minimal changes.
The problem is that your bn_tree_free_node_space function takes, as its argument, a copy of the pointer variable - that is, you are passing the pointer by value - thus, the line nodes = NULL; at the end of that function only sets the local variable to NULL and does not change the value of the node_processing_space variable.
To fix this (with minimal changes to your code logic1), you need to pass that function a pointer to the pointer, and dereference that in the function. So, your function should look like this:
void bn_tree_free_node_space(bn_tree_node** nodes) // Argument is pointer-to-pointer
{
int i = 0;
for (; i < BN_TREE_HEAP_SIZE; i++) {
if ((*nodes)[i].dataset) { // Now we need to use (*nodes) to get the underlying pointer
free((*nodes)[i].dataset); // ... same here
}
}
free(*nodes); /// ... and here
*nodes = NULL;
}
You will, of course, also need to change the function prototype (just before your main) to match the new definition:
void bn_tree_free_node_space(bn_tree_node** nodes); // Must match definition!
Fruther, you will (clearly) need to change the calls to that function to pass the address of the node_processing_space pointer:
bn_tree_free_node_space(&node_processing_space); // Likewise for the other 3 calls!
Feel free to ask for further clarification and/or explanation.
1 EDIT: There are other ways (some may argue better ways) to implement your system, and also other 'minor' issues in your code. However, you did explicitly ask for "concise answers with minimal changes," so I have endeavoured to comply with that request!
I'm struggling with one of my training tasks for getting in touch with a new language. Unfortunately, this time the new language is an old one, it is C. My programming Task for this is to generate Langford-Strings, which should not be the main problem.
My first attempt in C, with a recursive approach works like a charm:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int grade = 0;
const char* blank = "_";
const char* alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
void generate(int position, char* string) {
if (!string) {
string = calloc(grade*2+1, sizeof(char));
for (int i = 0; i < grade*2; i++) {
string = strcat(string, blank);
}
}
if (!strstr(string, blank)) {
printf("%s\n", string);
return;
}
if (position < strlen(string)) {
if (string[position] != *blank) {
char* nstring = calloc(grade*2+1, sizeof(char));
strcpy(nstring, string);
generate(position+1, nstring);
free(nstring);
return;
} else {
for (int i = 0; i<strlen(string); i++) {
if (strchr(string, alphabet[i])){
continue;
}
int index = strcspn(alphabet, &alphabet[i])+1;
if (position+index+1<strlen(string)) {
if (string[position]==*blank) {
if (string[position+index+1]==*blank) {
char* nstring = calloc(grade*2+1, sizeof(char));
strncat(nstring, string, position);
strncat(nstring, &alphabet[i], 1);
strncat(nstring, &string[position+1], index);
strncat(nstring, &alphabet[i], 1);
strcat(nstring, &string[position+2+index]);
if (position<strlen(nstring)) {
generate(position+1, nstring);
}
free(nstring);
}
}
}
}
}
}
}
int main(int argc, char* argv[]) {
if (argc < 2) {
printf("Missing parameter of langford strings grade!\n");
return 1;
}
grade = strtol(argv[1], NULL, 10);
if (grade % 4 != 0) {
if ((grade+1) % 4 != 0) {
printf("Grade must be multiple of 4 or one less\n");
return 1;
}
}
generate(0, NULL);
return 0;
}
That works great, giving me exactly the results I expected.
But when I try to do it threaded (old-style threaded, spawning a new thread on each level of the recursion), it not only ends with a seqfault every time. It does end in an seqfault in a not predictable time. That means, that it runs indefinitly, printing out doubled and trippled results and always a random number of results, before seqfaulting.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <errno.h>
size_t grade = 0;
const char* blank = "_";
const char* alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
struct thread {
pthread_t* thread;
size_t position;
struct thread* threads[26];
char* string;
};
void* alloc_thread_data() {
struct thread* ret = calloc(1, sizeof(struct thread));
ret->thread = calloc(1, sizeof(pthread_t));
ret->position = 0;
ret->string = calloc((grade*2)+1, sizeof(char));
return (void*)ret;
}
void free_thread_data(struct thread* dst) {
free(dst->string);
free(dst->thread);
free(dst);
}
void assemble_string(char* dst, char* src, size_t pos, size_t index) {
strncat(dst, src, pos);
strncat(dst, &alphabet[index-1], 1);
strncat(dst, &src[pos+1], index);
strncat(dst, &alphabet[index-1], 1);
strncat(dst, &src[pos+2+index], (grade*2)-pos+index+2);
}
void* generate(void* data) {
struct thread* args = (struct thread*)data;
if (args->string && strlen(args->string)==0) {
for (size_t i = 0; i<grade*2; i++) {
strcat(args->string, blank);
}
}
if (args->string && !strstr(args->string, blank)) {
printf("%s\n", args->string);
return NULL;
}
if (args->string && args->position<strlen(args->string)) {
size_t sub = 0;
if (args->string[args->position]!=*blank) {
args->threads[sub] = alloc_thread_data();
strcpy(args->threads[sub]->string, args->string);
args->threads[sub]->position = args->position+1;
pthread_create(args->threads[sub]->thread, NULL, generate, (void*)args->threads[sub]);
sub++;
} else {
for (size_t i = 0; i<grade*2; i++) {
if (strchr(args->string, alphabet[i])){
continue;
}
int index = strcspn(alphabet, &alphabet[i])+1;
if (args->string[args->position] == *blank) {
if (args->string[args->position+index+1] == *blank) {
args->threads[sub] = alloc_thread_data();
assemble_string(args->threads[sub]->string, args->string, args->position, index);
args->threads[sub]->position = args->position+1;
pthread_create(args->threads[sub]->thread, NULL, generate, (void*)args->threads[sub]);
sub++;
}
}
}
}
for (size_t i = 0; i<sub; i++) {
if (args->threads[i]->thread!=NULL) {
if(pthread_kill(*args->threads[i]->thread, 0)==0) {
pthread_join(*args->threads[i]->thread, NULL);
}
free_thread_data(args->threads[i]);
}
}
}
return NULL;
}
int main(int argc, char* argv[]) {
if (argc < 2) {
printf("Missing parameter of langford strings grade!\n");
return 1;
}
grade = strtol(argv[1], NULL, 10);
if (grade % 4 != 0) {
if ((grade+1) % 4 != 0) {
printf("Grade must be multiple of 4 or one less\n");
return 2;
}
}
struct thread* args = alloc_thread_data();
pthread_create(args->thread, NULL, generate, (void*)args);
if(pthread_kill(*args->thread, 0)==0) {
pthread_join(*args->thread, NULL);
}
free_thread_data(args);
}
So, as written before, I managed to get around C programming for my whole work-life and do this just for fun - so I do not expect my code to be somewhat comprehensive. Please help me finding out, what is wrong with the threaded approach (and if you see any well-known-codesmell in the first one as well of course). Any hints welcome.
In addition to the bad allocation that #EugeneSh. pointed out, this looks like a problem:
pthread_create(args->threads[sub]->thread, NULL, generate,
(void*)&args->threads[sub]);
Note the difference from this other call that also appears:
pthread_create(args->threads[sub]->thread, NULL, generate,
(void*)args->threads[sub]);
[newlines inserted and indentation normalized for clarity and ease of reading].
args->threads[sub] is a struct thread*. You want to pass that pointer itself to pthread_create(), as in the second case, not its address, as in the first case.
Overall, I'm inclined to agree with #MikeRobinson that yours is an inappropriate use of threads. It is never useful performance-wise to have more schedulable threads in your process than you have cores, and you scale up to many thousands of total threads very quickly. I doubt very much that the result will outperform your single-threaded solution -- the costs of the context switching and cache thrashing that surely result will likely swamp whatever speedup you get from parallel execution on the 4 - 12 cores you probably have.
Added:
Additionally, it is very important to check the values returned by your function calls for error codes, unless you don't care and don't need to care whether the calls succeed. In particular, you should check
the return values of your malloc() / calloc() calls -- these return NULL in the event of unsuccessful allocation, and with as many total allocations as you perform, it is plausible that some of these fail. Using the resulting NULL pointer could easily lead to a segfault
the return values of your pthread_create() calls -- these return a value different from 0 in the event of failure. It is not safe to afterward rely on pthread_kill() to determine whether the thread was created successfully, for a failed pthread_create() leaves the thread handle's contents undefined. Any subsequent evaluation that depends on the value of the handle therefore exhibits undefined behavior.
I'm also a little suspicious of all your strncat()ing, for this is a notorious source of string overruns. These are ok if the target strings have enough capacity, but it's difficult for me to tell whether they always do in your case.
May I cordially suggest that it makes absolutely no sense (to me, at least ...) to "spawn a new thread" here?
The only reason to "spawn a thread" is when you wish to perform two thereafter, independent activities, which this algorithm quite clearly does not.
The immediate reason for the segfault is that the various threads are all attempting to manipulate the same data without regard to one another, and without waiting for one another. But, IMHO, the root cause of the problem is ... that this entire scenario is nonsense. "Recursion" and "multi-threading" are not at all the same thing. If your objective here was to learn about threading, I'm afraid that you've just learned far more (the very-hard way) than you ever wished to know . . .
I'm not proficient in C programming so please excuse me if this isn't a strong question. In the following code, I can only allocate memory to samplesVecafter obtaining the value of nsamplepts, but I need to return the vector samplesVec to the main for further use (not yet coded). However, I'm getting the following error:
Error in Terminal Window:
ImportSweeps(3497,0x7fff7b129310) malloc: * error for object 0x7fdaa0c03af8: pointer being freed was not allocated
* set a breakpoint in malloc_error_break to debug
Abort trap: 6
I'm using Mac OS X Mavericks with the gcc compiler. Thanks for any help.
*EDITED!!! AFTER VALUABLE INPUTS FROM COMMENTATORS, THE FOLLOWING REPRESENTS A SOLUTION TO THE ORIGINAL PROBLEM (WHICH IS NO LONGER AVAILABLE) *
The following code modification seemed to solve my original questions. Thanks for the valuable inputs everyone!
/* Header Files */
#define LIBAIFF_NOCOMPAT 1 // do not use LibAiff 2 API compatibility
#include <libaiff/libaiff.h>
#include <unistd.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <math.h>
/* Function Declarations */
void FileSearch(char*, char*, char*, char*, char*);
int32_t *ImportSweeps(char*);
/* Main */
int main()
{
char flag1[2] = "N";
char binname[20] = "bin1"; // dummy assignment
char buildfilename[40] = "SweepR";
char skeletonpath[100] = "/Users/.../Folder name/";
int k, len;
/* Find the sweep to be imported in the directory given by filepath */
FileSearch(skeletonpath, binname, buildfilename, skeletonpath, flag1);
if (strcmp(flag1,"Y")) {
printf("No file found. End of program.\n");
} else {
len = (int) strlen(skeletonpath);
char *filepath = malloc(len);
for (k = 0; k < len; k++) {
filepath[k] = skeletonpath[k];
}
printf("File found! Filepath: %s\n", filepath);
// Proceed to import sweep
int32_t *sweepRfile = ImportSweeps(filepath);
if (sweepRfile) {
printf("Success!\n");
// Do other things with sweepRfile
free(sweepRfile);
}
free(filepath);
}
return 0;
}
/* Sub-Routines */
void FileSearch(char *dir, char *binname, char *buildfilename, char* filepath, char* flag1)
{
DIR *dp;
struct dirent *entry;
struct stat statbuf;
if((dp = opendir(dir)) == NULL) {
fprintf(stderr,"Cannot open directory: %s\n", dir);
return;
}
chdir(dir);
while((entry = readdir(dp)) != NULL) {
lstat(entry->d_name, &statbuf);
if(S_ISDIR(statbuf.st_mode)) {
/* Found a directory, but ignore . and .. */
if(strcmp(".",entry->d_name) == 0 || strcmp("..",entry->d_name) == 0)
continue;
strcpy(binname,entry->d_name);
strcpy(buildfilename,"SweepR");
/* Recurse at a new indent level */
FileSearch(entry->d_name, binname, buildfilename, filepath, flag1);
}
else {
sprintf(buildfilename, "%s%s.aiff", buildfilename, binname);
if (strcmp(entry->d_name,buildfilename)) {
strcpy(buildfilename,"SweepR");
} else {
sprintf(filepath, "%s%s/%s", filepath, binname, buildfilename);
strcpy(flag1,"Y");
break;
}
}
}
chdir("..");
closedir(dp);
}
int32_t *ImportSweeps(char *filepath)
{
char *filepathread = filepath;
/* Initialize files for importing */
AIFF_Ref fileref;
/* Intialize files for getting information about AIFF file */
uint64_t nSamples;
int32_t *samples = NULL;
int32_t *samplesVec = NULL;
int channels, bitsPerSample, segmentSize, ghost, nsamplepts;
double samplingRate;
/* Import Routine */
fileref = AIFF_OpenFile(filepathread, F_RDONLY) ;
if(fileref)
{
// File opened successfully. Proceed.
ghost = AIFF_GetAudioFormat(fileref, &nSamples, &channels, &samplingRate, &bitsPerSample, &segmentSize);
if (ghost < 1)
{
printf("Error getting audio format.\n");
AIFF_CloseFile(fileref); return (int32_t) 0;
}
nsamplepts = ((int) nSamples)*channels;
samples = malloc(nsamplepts * sizeof(int32_t));
samplesVec = malloc(nsamplepts * sizeof(int32_t));
ghost = AIFF_ReadSamples32Bit(fileref, samples, nsamplepts);
if (ghost) {
for (int k = 0; k < nsamplepts; k++) {
samplesVec[k] = *(samples+k);
}
}
free(samples);
AIFF_CloseFile(fileref);
}
return samplesVec;
}
So... as far as I can see... :-)
samplesVec, the return value of ImportSweeps is not initialized, if fileref is false. Automatic (== local) variables have no guarantees on its value if samplesVec are not explicitly initialized - in other words samplesVec could carry any address. If samplesVec is not NULL on luck (which on the other hand might be often the case), you try free a not allocated junk of memory, or by very bad luck an somewhere else allocated one.
If I'm correct with my guess you can easy fix this with:
int32_t *samples;
int32_t *samplesVec = NULL;
It is a good idea anyway to initialize any variable as soon as possible with some meaningful error or dummy value, if you not use it in the very next line. As pointers are horrible beasts, I always NULL them if I don't initialize them with a useful value on declaration.
Edit: Several minor small changes for a readable approximation to English. :-)
If AIFF_OpenFile fails, ImportSweeps returns an undefined value because samplesVec wasn't initialized. If that value is non-NULL, main will try to free it. You can either initialize samplesVec = NULL, or you can reorganize the code as
fileref = AIFF_OpenFile(filepathread, F_RDONLY) ;
if(!fileref) {
{
// print error message here
return NULL;
}
// File opened successfully. Proceed.
...
There are people who will insist a functon that should only have one exit -- they are poorly informed and voicing a faulty dogma handed down from others who are likewise uninformed and dogmatic. The check for error and return above is known as a guard clause. The alternate style, of indenting every time a test succeeds, yields the arrow anti-pattern that is harder to read, harder to modify, and more error prone. See http://blog.codinghorror.com/flattening-arrow-code/ and http://c2.com/cgi/wiki?ArrowAntiPattern for some discussion.
I have the following code:
for(i = 0 ; i < max_thread; i++)
{
struct arg_struct args;
args.arg1 = file;
args.arg2 = word;
args.arg3 = repl;
if(pthread_create(&thread_id[i],NULL,&do_process,&args) != 0)
{
i--;
fprintf(stderr,RED "\nError in creating thread\n" NONE);
}
}
for(i = 0 ; i < max_thread; i++)
if(pthread_join(thread_id[i],NULL) != 0)
{
fprintf(stderr,RED "\nError in joining thread\n" NONE);
}
int do_process(void *arguments)
{
//code missing
}
*How can I transform (void *)do_process into (int) do_process ?*
That function returns very important info and without those returns I don't know how to read the replies
I get the following error: warning: passing arg 3 of `pthread_create' makes pointer from integer without a cast
The thread function returns a pointer. At minimum, you can allocate an integer dynamically and return it.
void * do_process (void *arg) {
/* ... */
int *result = malloc(sizeof(int));
*result = the_result_code;
return result;
}
Then, you can recover this pointer from the thread_join() call;
void *join_result;
if(pthread_join(thread_id[i],&join_result) != 0)
{
fprintf(stderr,RED "\nError in joining thread\n" NONE);
} else {
int result = *(int *)join_result;
free(join_result);
/* ... */
}
Just write a helper function that is of the correct type, but all it does is take the void * input parameter, get all the right parameters out of it, call your function, take the return of that, and package it up as a void * for pthread_join to get.
To your specific question, you can't/shouldn't. Just do what I outlined above and you'll be golden.
The pthread_join() is a simple way to communicate between the two threads. It has two limitations. First, it can pass only one value from the pointer (you can make it a pointer and store multiple values). Second, you can return it only when the thread is all done -- after returning this value, the thread goes in terminated state. So, if you want the threads to communicate in a more granular fashion, you will be better served in using a common shared data. Of course, at teh very least, you would to use Pthread mutex to synchronize access to the common data. And, if you want the threads to communicate with each other, then you would also need to use Pthread condvars.
I can't seem to find the cause of the segmentation fault in the code below. The code crashes in the line with the comment, after jumping back to main() from evaluate(). I looked at this code with gdb, and it seems everything is ok within the evaluate function - Output is filled in correctly, however after I return to main, nothing in the memory can be accessed. "p j" in gdb return an memory not accessible error, as does trying to print any other variable. I checked if the stack maybe had to many values, but increasing the stack size does not help.
The crazy thing is that I can solve this error, but I have no idea why it changes anything. If I add, anywhere in evaluate(), an int declaration (eg. int iAmNotUsedEver;) then the code suddenly doesn't cause a segmentation error and works perfectly in gdb.
Edit: Dynamically allocating node[116] in evaluate (int *node = malloc(116*sizeof(int));) also resolves the issue, but again I have no idea why?
Evaluate function: (I removed part of the code, otherwise it would be way too long)
void evaluate(int *parameter, int output[][16]) {
int node[116];
node[0] = 0;
output[0][0] = !node[0];
node[1] = parameter[0];
output[0][1] = !node[1];
output[0][2] = !node[0];
...
node[34] = !node[114] && !node[45];
node[45] = !node[34] && !node[105];
output[11][15] = node[45];
}
Main function:
int main(int argc, char *argv[]) {
int i;
int j;
int k;
int ret;
int parameter[8];
int output[12][16];
FILE *paramFile;
FILE *outFile;
paramFile = fopen(argv[1], "r");
if (paramFile == NULL) {
printf("I couldn't open the parameter file.\n");
exit(0);
}
outFile = fopen(argv[2], "ab");
if (outFile == NULL) {
printf("Something went wrong with the output file.\n");
exit(0);
}
while(1){
for(i=0;i<8;i++){
ret=fscanf(paramFile, "%d", ¶meter[i]);
printf("parameter: %d\n", parameter[i]);
}
if(ret!=1){
break;
}
for(j=0;j<12;j++){
for(k=0;k<16;k++){
output[j][k] = 2;
}
}
evaluate(parameter,output);
printf("Evaluation is done. \t %d\n",i);
for(j=0;j<12;j++){ //SEG FAULT HERE
for(k=0;k<16;k++){
fprintf(outFile, "%d", output[j][k]);
}
fprintf(outFile,"\n");
}
fprintf(outFile,"\n\n\n");
}
printf("Closing files\n");
fclose(paramFile);
fclose(outFile);
return 0;
}
First of all you should check if there is at least two argument. This can provoque a segmentation fault too.
if (argc < 3)
return -1;
Then, in evaluate, as they are fixe array you should pass in parameter
void evaluate(int parameter[8], int output[12][16]) {
...
}
You can also do this
void evaluate(int *parameter, int **output) {
...
}
I think the segfault can be avoid using one on this two declaration.