Let's say I have a function to perform a small and particular task that has a fairly good possibility of failure. What is the best way to handle something going wrong? (Assuming I know what the problem is).
For example lets say I have a function that reads a two byte string and returns it:
#include <stdio.h>
#include <stdlib.h>
char *bar(void)
{
char *foo = malloc(3);
scanf("%2s", foo);
return foo;
}
int main(void)
{
char *foo = bar();
puts(foo);
free(foo);
return 0;
}
The above example has absolutely no error handling whatsoever. There are two ways that I would implement some sort of error handling, but I'm not sure which would be more preferred or considered best practice.
Method 1 (print error message To stderr from within the function):
#include <stdio.h>
#include <stdlib.h>
char *bar(void)
{
char *foo;
if(!(foo = malloc(3)))
{
fputs("\nError! Memory allocation failed.", stderr);
return 0x00;
}
scanf("%2s", foo);
return foo;
}
int main(void)
{
char *foo;
if(!(foo = bar())) return 1;
puts(foo);
free(foo);
return 0;
}
Method 2 (print error message to stderr from the calling function):
#include <stdio.h>
#include <stdlib.h>
char *bar(void)
{
char *foo;
if(!(foo = malloc(3))) return 0x00;
scanf("%2s", foo);
return foo;
}
int main(void)
{
char *foo;
if(!(foo = bar()))
{
fputs("\nError! Memory allocation failed.", stderr);
return 1;
}
puts(foo);
free(foo);
return 0;
}
I'm almost thinking that method two would be the best way to go because that way I could get more specific with my error messages depending on what I'm calling that function for at the time.
What I worry about with method two is the fact that I lose the ability to print what specifically went wrong in the function if it has more than one potential point of failure.
Pseudo Code:
IF FAILUREA
PRINT "FAILUREA OCCURED"
RETURN
IF FAILUREB
PRINT "FAILUREB OCCURED"
RETURN
This wouldn't be much of a problem if the function I was calling was an int because then I could just return a different integer value based on what went wrong. But in the case of a char* I typically try to return NULL on failure (so both FAILUREA and FAILUREB would be returning NULL); there would be no way to know what caused the function to fail.
So my question is what is best practice when it comes to handling error messages?
Allowing the caller to handle error reporting is better because:
if the function is forming part of a library stderr may not be available and an alternative reporting mechanism is required.
the calling code may have an alternative action that can be taken and may not deem the failure of function bar() as an actual failure and have no need to report it.
If a function has multiple possible failure reasons then a possibility is to pass an argument to the function that is updated in the event of failure. The calling function can then choose an appropriate action depending on the actual failure reason. For example:
enum Status
{
STATUS_OK,
STATUS_MEMORY_ALLOCATION_FAILURE,
STATUS_ACCESS_DENIED
};
enum Status status;
char* foo = bar(&status);
if (!foo)
{
if (STATUS_MEMORY_ALLOCATION_FAILURE == status)
{
/* report failure. */
}
else if (STATUS_ACCESS_DENIED == status)
{
/* try somewhere else */
}
}
If you can do anything about a failure and if you are going to, then you do it.
Otherwise, you may implement a generic failure function, call it in case of an error and call it a day:
void error(const char* format, ...)
{
va_list vl;
va_start(vl, format);
vfprintf(stderr, format, vl);
va_end(vl);
exit(-1);
}
You can optionally wrap it in a macro supplying it with the line# and file name:
#define ERROR(fmt, ...) \
error("file:'%s',line:%d " fmt, __FILE__, __LINE__, __VA_ARGS__)
This will make errors in the console very easy to figure out because the error messages tell precisely the file and the line in it where the error has occurred.
Typical usage, nothing fancy:
char *bar(void)
{
char *foo;
if ((foo=malloc(3)) == NULL)
ERROR("malloc() failed!\n");
if (scanf("%2s", foo) != 1)
ERROR("scanf() failed!\n");
return foo;
}
You may use longjmp() in place of exit(-1) to immediately return to the caller (=the one that did the respective setjmp()) if you want to actually do something upon the error, maybe close all files open for writing, so the buffered data isn't lost.
If you're writing a simple compiler, for example, this kind of error() is more than enough for most errors internal to the compiler and for problems in the source code being compiled (e.g. a missing colon/paren or something else that makes the code not compilable).
If you cannot or do not want to do any of that, you need to carefully write the code, do proper clean ups and return different error codes to communicate actionable errors to the caller.
You can do in this way if your function return more than 1 error case
#include <stdio.h>
#include <stdlib.h>
int bar(char **foo)
{
if(!(malloc(3))) return 1; /* return error case 1*/
scanf("%2s", *foo);
if(!(malloc(4))) return 2; /* return error case 2*/
return 0; /* no error*/
}
int catcherror(int error)
{
switch (error) {
case 1:
/*do something 1*/
case 2:
/*do something 1*/
case 3:
/*do something 1*/
case 4:
/*do something 1*/
case 5:
/*do something 1*/
default:
/*do something 1*/
}
}
int main(void)
{
char *foo;
int error
error = bar(&foo);
catcherror(error);
puts(foo);
free(foo);
return 0;
}
The catcherror() function could be very useful if your project contains many functions which return a common error cases
Related
I using g++ on linux with eclipse. I am making a code that get file's time and output file's month,hour,etc...
While debugging, the value of time1 changed unexpectedly but I have no idea about this issue.
What is problem of this code?
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
struct stat stat1, stat2;
struct tm *time1, *time2;
void filestat1(void);
void filestat2(void);
void filetime1(void);
void filetime2(void);
void datecmp(void);
void timecmp(void);
int main(void)
{
filestat1();
filestat2();
filetime1();
filetime2();
datecmp();
timecmp();
}
void filestat1(void)
{
// check if there is no text1
int check = 0;
check = stat("text1", &stat1);
if(check != 0)
{
printf("Error : there is no text1\n");
}
return;
}
void filestat2(void)
{
// check if there is no text2
int check = 0;
check = stat("text2", &stat2);
if(check != 0)
{
printf("Error : there is no text2\n");
}
return;
}
void filetime1(void)
{
time1 = localtime(&stat1.st_mtime); //!!! this change unexpectedly
return;
}
void filetime2(void)
{
time2 = localtime(&stat2.st_mtime);
return;
}
void datecmp(void)
{
printf("date compare\n");
// compare tm_mon
if(time1->tm_mon > time2->tm_mon)
printf("time1 is early \n");
else if(time1->tm_mon < time2->tm_mon)
printf("time2 is early \n");
else{
// compare tm_mday
if(time1->tm_mday > time2->tm_mday)
printf("time1 is early \n");
else if(time1->tm_mday < time2->tm_mday)
printf("time2 is early \n");
// same date
else
printf("same time \n");
}
printf("\n");
}
void timecmp(void)
{
printf(time1->tm_hour);
printf(time2->tm_hour);
printf("time compare\n");
// compare hour
if(time1->tm_hour > time2->tm_hour)
printf("time1 is early \n");
else if(time1->tm_hour < time2->tm_hour)
printf("time2 is early \n");
else{
// compare minutes
if(time1->tm_min > time2->tm_min)
printf("time1 is early \n");
else if(time1->tm_min < time2->tm_min)
printf("time2 is early \n");
// same time
else
printf("same time \n");
}
}
localtime returns a pointer to a static structure. You need to copy the result before calling localtime again.
I would declare time1 and time2 as structures instead of pointers to store the values.
struct tm time1, time2;
void filetime1(void)
{
struct tm *tmp = localtime(&stat1.st_mtime);
if (tmp == NULL) {
//... handle error
}
time1 = *tmp;
}
Similarly for filetime2.
If you are writing multi-threaded code, it is safer to use the reentrant variant of the function, localtime_r. In that case, you pass in the pointer to the structure for the result.
void filetime1(void)
{
struct tm *tmp = localtime_r(&stat1.st_mtime, &time1);
if (tmp == NULL) {
//... handle error
} else {
assert(tmp == &time1);
}
}
You are using global variables, completely unnecessarily, making your life harder than it has to be. It is very hard for us humans to track where global variables are modified, especially when you have several global variables with very similar names.
So, instead of trying to unravel all that, let's rewrite it using function parameters, without any global variables at all.
First, we tell the C library we want POSIX.1-2008 features, and include the headers that expose the functionality we need:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <limits.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
Next, let's define a function that takes a file name as a parameter, and pointers to where the function can store the last access and last modification timestamps. If the function is successful, it'll return 0; otherwise, it'll return -1 with errno set to indicate the error.
int filetime(const char *path,
time_t *accessed, long *accessed_nsec,
time_t *modified, long *modified_nsec)
{
struct stat info;
/* Path must not be NULL or empty. */
if (!path || !path[0]) {
errno = EINVAL;
return -1;
}
/* Get file statistics. */
if (stat(path, &info) == -1)
return -1; /* errno was set by stat() */
/* Save timestamps. */
if (accessed)
*accessed = info.st_atim.tv_sec;
if (accessed_nsec)
*accessed_nsec = info.st_atim.tv_nsec;
if (modified)
*modified = info.st_mtim.tv_sec;
if (modified_nsec)
*modified_nsec = info.st_mtim.tv_nsec;
/* Success. */
return 0;
}
Let's continue by writing a simple main(), that takes one or more file names as command-line parameters, and describes them. I like to start the main by checking the number of command line arguments, and if specified, the first argument. If none, or the first one is -h or --help, I like to print the usage of the utility. This way, I can keep my example programs in their own directories, and to find one I'm looking for, I can just execute each one without parameters, to see what each of them does. It's much faster than reading the sources!
int main(int argc, char *argv[])
{
int arg;
if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s FILENAME ...\n", argv[0]);
fprintf(stderr, "\n");
fprintf(stderr, "This program will print the last access and\n");
fprintf(stderr, "last modification timestamps for each of\n");
fprintf(stderr, "the specified files.\n");
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
Because there is at least one, but possibly more than one file name parameter, we handle each of them in a loop:
for (arg = 1; arg < argc; arg++) {
time_t accessed, modified;
long accessed_ns, modified_ns;
struct tm accessed_localtime, modified_localtime;
Within the loop, we first call our filetime() function. Note how we have declared the variables we want filled above, and how we call the function: &accessed yields a pointer to accessed.
if (filetime(argv[arg], &accessed, &accessed_ns,
&modified, &modified_ns)) {
/* Nonzero return value, so an error occurred! */
fprintf(stderr, "%s: %s.\n", argv[arg], strerror(errno));
return EXIT_FAILURE;
}
In C, function parameters are passed by value, not by reference. This means that if a function parameter is say int foo, any changes to foo within the function are visible within the function only; the changes are not visible to the caller. When we pass a pointer to a variable, say int *foo, then changes to foo are still visible only within the function, but *foo refers to the value pointed at by the pointer; and changes to that are visible to the caller.
In short, when we want a function to be able to modify the value of a variable, we use a pointer. That's just how C works.
Now that we have the times in Unix Epoch time (time_t), we want to split them into local time fields:
if (!localtime_r(&accessed, &accessed_localtime) ||
!localtime_r(&modified, &modified_localtime)) {
fprintf(stderr, "%s: Cannot compute timestamps in local time: %s.\n", argv[arg], strerror(errno));
return EXIT_FAILURE;
}
Note that I again used a POSIX.1-2008 function, localtime_r(). In tutorials, you often see the older localtime() used instead, but that one may use a global variable internally (it can always return a pointer to the same structure, reusing it for every call); localtime_r() is better.
See how the second parameter to localtime_r is also a pointer (to a struct tm)? Again, this is just how you do functions that change some values in a way that is visible to the caller.
Also, it is rare for localtime_r() (or localtime()) to fail, so many simply ignore checking it for errors. There is no excuse for that, as it's just a couple of lines more code, and if an error does occur at some point, the user will be immensely more satisfied with a clear error code, rather than just seeing the program crash due to segmentation fault.
All that is left, is to print out the information gathered. I like to use a variant the ISO 8601 international standard for the time format; in particular, it sorts in proper time order even if sorted alphabetically. (My variant is that I like to use a space, and not a T, between the date and the time.)
printf("%s:\n", argv[arg]); /* The file name or path */
printf(" Modified: %04d-%02d-%02d %02d:%02d:%02d.%03d\n",
modified_localtime.tm_year + 1900,
modified_localtime.tm_mon + 1,
modified_localtime.tm_mday,
modified_localtime.tm_hour,
modified_localtime.tm_min,
modified_localtime.tm_sec,
modified_ns / 1000000L);
printf(" Accessed: %04d-%02d-%02d %02d:%02d:%02d.%03d\n",
accessed_localtime.tm_year + 1900,
accessed_localtime.tm_mon + 1,
accessed_localtime.tm_mday,
accessed_localtime.tm_hour,
accessed_localtime.tm_min,
accessed_localtime.tm_sec,
accessed_ns / 1000000L);
/* Make sure everything written to stdout
is actually written to standard output right now. */
fflush(stdout);
}
return EXIT_SUCCESS;
}
The fflush(stdout) tells the C library to ensure all preceding writes to stdout are actually written to the standard output. (By default, stdout is buffered, and stderr is unbuffered.) Normally, the C library will flush the output at every newline, but having the explicit flush there also reminds us human programmers that we want everything thus far printed, to actually appear on the programs standard output at that point. (This way, if one of the files is on some slow filesystem, say old USB stick or a network share, the information on previous files gets shown before the program accesses the slow file. Essentially, the "stall" will occur at the expected place for the human users.)
It is probably a good idea to mention relatime and other related mount options at this point. In simple terms, it means that to avoid the number of writes to storage media due to read accesses, the access time is not always updated. So, if you don't see it changing even after you read a file (using e.g. cat FILENAME >/dev/null), it just means your system has mount options enabled that reduce the access time updates to speed up your filesystem access and reduce the number of writes to it. It is a good option; I use it.
Finally, most Linux filesystems do not have a created timestamp at all. The st_ctime (and st_ctim.tv_sec and st_ctim.tv_nsec) fields refer to last status change timestamp. It tracks changes to the owner, group, permissions, and the number of hard links.
When you examine the above code, especially the if clauses, it is useful to remember that in C, the logical OR operation, ||, is short-circuiting: the left side is evaluated first, but if it fails, the right side is not evaluated at all. So, if you have e.g. int x = 1, y = 0; and you do (x == 0 || ++y), y will not be incremented at all. I utilize this when examining argv[1] in the very first if clause in main().
This is a really long question due to the code snippets and the detailed explanations. TL;DR, are there issues with the macros shown below, is this a reasonable solution, and if not then what is the most reasonable way to solve the issues presented below?
I am currently writing a C library which deals with POSIX threads, and must be able to handle thread cancellation cleanly. In particular, the library functions may be called from threads that were set to be cancellable (either PTHREAD_CANCEL_DEFFERED or PTHREAD_CANCEL_ASYNCHRONOUS canceltype) by the user.
Currently the library functions that interface with the user all begin with a call to pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate), and at each return point, I make sure that a call to pthread_setcancelstate(oldstate, &dummy) is made to restore whatever cancellation settings the thread had previously.
This basically prevents the thread from being cancelled while in the library code, thus ensuring that the global state remains consistent and resources were properly managed before returning.
This method unfortunately has a few drawbacks:
One must be sure to restore the cancelstate at every return point. This makes it somewhat hard to manage if the function has nontrivial control flow with multiple return points. Forgetting to do so may lead to threads that don't get cancelled even after return from the library.
We only really need to prevent cancellations at points where resources are being allocated or global state is inconsistent. A library function may in turn call other internal library functions that are cancel-safe, and ideally cancellations could occur at such points.
Here is a sample illustration of the issues:
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
static void do_some_long_computation(char *buffer, size_t len)
{
(void)buffer; (void)len;
/* This is really, really long! */
}
int mylib_function(size_t len)
{
char *buffer;
int oldstate, oldstate2;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
buffer = malloc(len);
if (buffer == NULL) {
pthread_setcancelstate(oldstate, &oldstate2);
return -1;
}
do_some_long_computation(buffer, len);
fd = open("results.txt", O_WRONLY);
if (fd < 0) {
free(buffer);
pthread_setcancelstate(oldstate, &oldstate2);
return -1;
}
write(fd, buffer, len); /* Normally also do error-check */
close(fd);
free(buffer);
pthread_setcancelstate(oldstate, &oldstate2);
return 0;
}
Here it is not so bad because there are only 3 return points. One could possibly even restructure the control flow in such a way as to force all paths to reach a single return point, perhaps with the goto cleanup pattern. But the second issue is still left unresolved. And imagine having to do that for many library functions.
The second issue may be resolved by wrapping each resource allocation with calls to pthread_setcancelstate that will only disable cancellations during resource allocation. While cancellations are disabled, we also push a cleanup handler (with pthread_cleanup_push). One could also move all resource allocations together (opening the file before doing the long computation).
While solving the second issue, it is still somewhat hard to maintain because each resource allocation needs to be wrapped under these pthread_setcancelstate and pthread_cleanup_[push|pop] calls. Also it might not always be possible to put all resource allocations together, for instance if they depend on the results of the computation. Moreover, the control flow needs to be changed because one cannot return between a pthread_cleanup_push and pthread_cleanup_pop pair (which would be the case if malloc returns NULL for example).
In order to solve both issues, I came up with another possible method that involves dirty hacks with macros. The idea is to simulate something like a critical section block in other languages, to insert a block of code in a "cancel-safe" scope.
This is what the library code would look like (compile with -c -Wall -Wextra -pedantic):
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include "cancelsafe.h"
static void do_some_long_computation(char *buffer, size_t len)
{
(void)buffer; (void)len;
/* This is really, really long! */
}
static void free_wrapper(void *arg)
{
free(*(void **)arg);
}
static void close_wrapper(void *arg)
{
close(*(int *)arg);
}
int mylib_function(size_t len)
{
char *buffer;
int fd;
int rc;
rc = 0;
CANCELSAFE_INIT();
CANCELSAFE_PUSH(free_wrapper, buffer) {
buffer = malloc(len);
if (buffer == NULL) {
rc = -1;
CANCELSAFE_BREAK(buffer);
}
}
do_some_long_computation(buffer, len);
CANCELSAFE_PUSH(close_wrapper, fd) {
fd = open("results.txt", O_WRONLY);
if (fd < 0) {
rc = -1;
CANCELSAFE_BREAK(fd);
}
}
write(fd, buffer, len);
CANCELSAFE_POP(fd, 1); /* close fd */
CANCELSAFE_POP(buffer, 1); /* free buffer */
CANCELSAFE_END();
return rc;
}
This resolves both issues to some extent. The cancelstate settings and cleanup push/pop calls are implicit in the macros, so the programmer only has to specify the sections of code that need to be cancel-safe and what cleanup handlers to push. The rest is done behind the scenes, and the compiler will make sure each CANCELSAFE_PUSH is paired with a CANCELSAFE_POP.
The implementation of the macros is as follows:
#define CANCELSAFE_INIT() \
do {\
int CANCELSAFE_global_stop = 0
#define CANCELSAFE_PUSH(cleanup, ident) \
do {\
int CANCELSAFE_oldstate_##ident, CANCELSAFE_oldstate2_##ident;\
int CANCELSAFE_stop_##ident;\
\
if (CANCELSAFE_global_stop)\
break;\
\
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &CANCELSAFE_oldstate_##ident);\
pthread_cleanup_push(cleanup, &ident);\
for (CANCELSAFE_stop_##ident = 0; CANCELSAFE_stop_##ident == 0 && CANCELSAFE_global_stop == 0; CANCELSAFE_stop_##ident = 1, pthread_setcancelstate(CANCELSAFE_oldstate_##ident, &CANCELSAFE_oldstate2_##ident))
#define CANCELSAFE_BREAK(ident) \
do {\
CANCELSAFE_global_stop = 1;\
pthread_setcancelstate(CANCELSAFE_oldstate_##ident, &CANCELSAFE_oldstate2_##ident);\
goto CANCELSAFE_POP_LABEL_##ident;\
} while (0)
#define CANCELSAFE_POP(ident, execute) \
CANCELSAFE_POP_LABEL_##ident:\
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &CANCELSAFE_oldstate_##ident);\
pthread_cleanup_pop(execute);\
pthread_setcancelstate(CANCELSAFE_oldstate_##ident, &CANCELSAFE_oldstate2_##ident);\
} while (0)
#define CANCELSAFE_END() \
} while (0)
This combines several macro tricks that I have encountered before.
The do { } while (0) pattern is used to have a multiline function-like macro (with semicolon required).
The CANCELSAFE_PUSH and CANCELSAFE_POP macros are forced to come in pairs by the use of the same trick as the pthread_cleanup_push and pthread_cleanup_pop using unmatched { and } braces respectively (here it is unmatched do { and } while (0) instead).
The usage of the for loops is somewhat inspired by this question. The idea is that we want to call the pthread_setcancelstate function after the macro body to restore cancellations after the CANCELSAFE_PUSH block. I use a stop flag that is set to 1 at the second loop iteration.
The ident is the name of the variable that will be released (this needs to be a valid identifier). The cleanup_wrappers will be given its address, which will always be valid in a cleanup handler scope according to this answer. This is done because the value of the variable is not yet initialized at the point of cleanup push (and also doesn't work if the variable is not of pointer type).
The ident is also used to avoid name collisions in the temporary variables and labels by appending it as a suffix with the ## concatenation macro, giving them unique names.
The CANCELSAFE_BREAK macro is used to jump out of the cancelsafe block and right into the corresponding CANCELSAFE_POP_LABEL. This is inspired by the goto cleanup pattern, as mentioned here. It also sets the global stop flag.
The global stop is used to avoid cases were there might be two PUSH/POP pairs in the same scope level. This seems like an unlikely situation, but if this happens then the content of the macros is basically skipped when the global stop flag is set to 1. The CANCELSAFE_INIT and CANCELSAFE_END macros aren't crucial, they just avoid the need to declare the global stop flag ourselves. These could be skipped if the programmer always does all the pushes and then all the pops consecutively.
After expanding the macros, we obtain the following code for the mylib_function:
int mylib_function(size_t len)
{
char *buffer;
int fd;
int rc;
rc = 0;
do {
int CANCELSAFE_global_stop = 0;
do {
int CANCELSAFE_oldstate_buffer, CANCELSAFE_oldstate2_buffer;
int CANCELSAFE_stop_buffer;
if (CANCELSAFE_global_stop)
break;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &CANCELSAFE_oldstate_buffer);
pthread_cleanup_push(free_wrapper, &buffer);
for (CANCELSAFE_stop_buffer = 0; CANCELSAFE_stop_buffer == 0 && CANCELSAFE_global_stop == 0; CANCELSAFE_stop_buffer = 1, pthread_setcancelstate(CANCELSAFE_oldstate_buffer, &CANCELSAFE_oldstate2_buffer)) {
buffer = malloc(len);
if (buffer == NULL) {
rc = -1;
do {
CANCELSAFE_global_stop = 1;
pthread_setcancelstate(CANCELSAFE_oldstate_buffer, &CANCELSAFE_oldstate2_buffer);
goto CANCELSAFE_POP_LABEL_buffer;
} while (0);
}
}
do_some_long_computation(buffer, len);
do {
int CANCELSAFE_oldstate_fd, CANCELSAFE_oldstate2_fd;
int CANCELSAFE_stop_fd;
if (CANCELSAFE_global_stop)
break;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &CANCELSAFE_oldstate_fd);
pthread_cleanup_push(close_wrapper, &fd);
for (CANCELSAFE_stop_fd = 0; CANCELSAFE_stop_fd == 0 && CANCELSAFE_global_stop == 0; CANCELSAFE_stop_fd = 1, pthread_setcancelstate(CANCELSAFE_oldstate_fd, &CANCELSTATE_oldstate2_fd)) {
fd = open("results.txt", O_WRONLY);
if (fd < 0) {
rc = -1;
do {
CANCELSAFE_global_stop = 1;
pthread_setcancelstate(CANCELSAFE_oldstate_fd, &CANCELSAFE_oldstate2_fd);
goto CANCELSAFE_POP_LABEL_fd;
} while (0);
}
}
write(fd, buffer, len);
CANCELSAFE_POP_LABEL_fd:
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &CANCELSAFE_oldstate_fd);
pthread_cleanup_pop(1);
pthread_setcancelstate(CANCELSAFE_oldstate_fd, &CANCELSAFE_oldstate2_fd);
} while (0);
CANCELSAFE_POP_LABEL_buffer:
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &CANCELSAFE_oldstate_buffer);
pthread_cleanup_pop(1);
pthread_setcancelstate(CANCELSAFE_oldstate_buffer, &CANCELSAFE_oldstate2_buffer);
} while (0);
} while (0);
return rc;
}
Now, this set of macros is horrendous to look at and it is somewhat tricky to understand how they work exactly. On the other hand, this is a one-time task, and once written, they can be left and the rest of the project can benefit from their nice benefits.
I would like to know if there are any issues with the macros that I may have overlooked, and whether there could be a better way to implement similar functionality. Also, which of the solutions proposed do you think would be the most reasonable? Are there other ideas that could work better to resolve these issues (or perhaps, are they really non-issues)?
Unless you use asynchronous cancellation (which is always very problematic), you do not have to disable cancellation around malloc and free (and many other POSIX functions). Synchronous cancellation only happens at cancellation points, and these functions are not.
You are abusing the POSIX cancellation handling facilities to implement a scope exit hook. In general, if you find yourself doing things like this in C, you should seriously consider using C++ instead. This will give you a much more polished version of the feature, with ample documentation, and programmers will already have experience with it.
I've developed the producer / consumer problem in C and for some reason it doesn't compile. I'm getting the error message:
try1.c: In function ‘main’:
try1.c:19:21: warning: incompatible implicit declaration of built-in function ‘malloc’ [enabled by default]
BUFFER=(char *) malloc(sizeof(char) * BufferSize);
Please could someone identify the issue? Have tried for a while to fix this now however haven't had any luck.
#include <stdio.h>
#include <pthread.h>
#define BufferSize 10
void *Producer();
void *Consumer();
int BufferIndex=0;
char *BUFFER;
pthread_cond_t Buffer_Not_Full=PTHREAD_COND_INITIALIZER;
pthread_cond_t Buffer_Not_Empty=PTHREAD_COND_INITIALIZER;
pthread_mutex_t mVar=PTHREAD_MUTEX_INITIALIZER;
int main()
{
pthread_t ptid,ctid;
BUFFER=(char *) malloc(sizeof(char) * BufferSize);
pthread_create(&ptid,NULL,Producer,NULL);
pthread_create(&ctid,NULL,Consumer,NULL);
pthread_join(ptid,NULL);
pthread_join(ctid,NULL);
return 0;
}
void *Producer()
{
for(;;)
{
pthread_mutex_lock(&mVar);
if(BufferIndex==BufferSize)
{
pthread_cond_wait(&Buffer_Not_Full,&mVar);
}
BUFFER[BufferIndex++]='#';
printf("Produce : %d \n",BufferIndex);
pthread_mutex_unlock(&mVar);
pthread_cond_signal(&Buffer_Not_Empty);
}
}
void *Consumer()
{
for(;;)
{
pthread_mutex_lock(&mVar);
if(BufferIndex==-1)
{
pthread_cond_wait(&Buffer_Not_Empty,&mVar);
}
printf("Consume : %d \n",BufferIndex--);
pthread_mutex_unlock(&mVar);
pthread_cond_signal(&Buffer_Not_Full);
}
}
Thanks a lot for your help.
First of all, this question has nothing to do with producer/consumer. It has to do with the use of a function you didn't declare.
Historically, C allowed calling a function which was never declared. Since technically function declaration is not needed to call it, compiler gladly added instructions to call the unknown function. Allegedly, it allowed developers to save precious keystrokes. However, you need a function declaration to know it's return value, and compiler assumed that return value of such a function is int. And you are having just that - an implicitly declared malloc() with assumed return type of int.
Now, compiler knows what malloc() is. It is often built-in intrinstic function. However, compiler also knows that return value of said malloc() is void*, not int - and thus it complains.
Solution - get rid of implicit declaration, and make a habit of always including apropriate header files for every function you are using.
You also have issues with the way you are using conditional variables, but I would leave it for another question.
The producer should go to sleep when the buffer is full. Next time when the consumer removes data it notifies the producer and producer start producing data again. The consumer should go to sleep when a buffer is empty. Next time when producer adds data it notifies the consumer and consumer starts consuming data. This solution can be achieved using semaphores.
#include<stdio.h>
#include<stdlib.h>
#include <pthread.h>
#include <semaphore.h>
int mutex=1,full=0,empty=3,x=0;
int main()
{
int n;
void producer(void);
void consumer(void);
int waiting(int);
int signaling(int);
printf("\n1.Producer\n2.Consumer\n3.Exit");
while(1)
{
printf("\nEnter your choice:");
scanf("%d",&n);
switch(n)
{
case 1: if((mutex==1)&&(empty!=0))
producer();
else
printf("Buffer is full!!");
break;
case 2: if((mutex==1)&&(full!=0))
consumer();
else
printf("Buffer is empty!!");
break;
case 3:
exit(0);
break;
}
}
return 0;
}
int waiting(int s)
{
return (--s);
}
int signaling(int s)
{
return(++s);
}
void producer()
{
mutex=wait(&mutex);
full=signaling(full);
empty=wait(&empty);
x++;
printf("\nProducer produces the item %d",x);
mutex=signaling(mutex);
}
void consumer()
{
mutex=wait(&mutex);
full=wait(&full);
empty=signaling(empty);
printf("\nConsumer consumes item %d",x);
x--;
mutex=signaling(mutex);
}
Just include the stdlib.h library. This library will include the malloc() function.
You are using malloc without including the header file that declares it (stdlib.h).
Your usage (without that explicit declaration from stdlib.h) creates an "implicit declaration" which doesn't match the one the compiler knows about because yours returns char* and the proper malloc returns void*.
So add include <stdlib.h> and also note that you shouldn't cast the result of a malloc. See Do I cast the result of malloc?
the program is still very empty and "naked" because im just trying to get the logic out of the way before I actually start making functions. For some reason, whatever my argv[1] is, it always prints "The help message". What am I missing? I have a bad feeling about that for statement, but I dont know whats wrong with it.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void help()
{
printf("The help message\n");
exit(1);
}
void a()
{
printf("The a screen\n");
exit(1);
}
int main(int argc, char *argv[])
{
char recognised_commands[3] = {help(), a()};
int i;
if (argc != 2)
{
fprintf(stderr, "usage of sake: \"sake [option(s)]\"\nFor a full listing of all available commands type \"-help\" or \"--help\"\n\n");
exit(1);
}
for (i = recognised_commands[0]; i != recognised_commands; i++)
{
printf(argv[1]);
}
}
Edit 1: djikay: Fixed the -1 to 0,
Ricky: How do I correct the help() and the a() to only call the one the user inputs after the program name (EX: sake -a)? I also fixed the exit(0). Thanks
The line:
char recognised_commands[3] = {help(), a()};
causes both help and a to be called. help is called first, and it prints out the help message & exits the program.
char recognised_commands[3] = {help(), a()};
This line is definitely your issue. help() and a() are both being called, thus exiting your program.
Why are you trying to assign those function return values there? Both of the functions are void in return type, meaning they won't return anything anyways.
On a side note, calling exit() with 0 as the argument means your program exited without errors. I'd exit with 1 if it's because of an error (or even better, EXIT_SUCCESS and EXIT_FAILURE, respectively).
Given this code :
#include <stdio.h>
#include <assert.h>
void print_number(int* somePtr) {
assert (somePtr!=NULL);
printf ("%d\n",*somePtr);
}
int main ()
{
int a=1234;
int * b = NULL;
int * c = NULL;
b=&a;
print_number (c);
print_number (b);
return 0;
}
I can do this instead :
#include <stdio.h>
#include <assert.h>
void print_number(int* somePtr) {
if (somePtr != NULL)
printf ("%d\n",*somePtr);
// else do something
}
int main ()
{
int a=1234;
int * b = NULL;
int * c = NULL;
b=&a;
print_number (c);
print_number (b);
return 0;
}
So , what am I gaining by using assert ?
Regards
assert is to document your assumptions in the code. if statement is to handle different logical scenarios.
Now in your specific case, think from the point of view of the developer of the print_number() function.
For example when you write
void print_number(int* somePtr) {
assert (somePtr!=NULL);
printf ("%d\n",*somePtr);
}
you mean to say that,
In my print_number function I assume that always the pointer coming is not null. I would be very very surprised if this is null. I don't care to handle this scenario at all in my code.
But, if you write
void print_number(int* somePtr) {
if (somePtr != NULL)
printf ("%d\n",*somePtr);
// else do something
}
You seem to say that, in my print_number function, I expect people to pass a null pointer. And I know how to handle this situation and I do handle this with an else condition.
So, sometimes you will know how to handle certain situations and you want to do that. Then, use if.
Sometimes, you assume that something will not happen and you don't care to handle it. You just express your surprise and stop your program execution there with assert.
The difference is that assert is enabled only for debug build; it is not enabled for release build (i.e when NDEBUG is defined), which means in the release build there will be no check; as a result, your code will be little bit faster, compared to the one in which you use if condition which remains in the release build as well.
That means, assert is used to check common errors when you write the code, and catch them as soon as possible, in the development phase itself.
Lots of reasons:
Asserts are usually removed for release builds.
Asserts will report failure information to the client. if() does nothing by itself.
Because asserts are usually macros, you can also get code information about the failing assertion.
Assert is more semantically clear than if().
If assertion fails, you will see the output containing the failed assertion itself, plus the function and the line of the failed assert, something like:
test: main.cpp:9: int main(): Assertion `0==1' failed.
So, if your program crashes in runtime, you will see the exact reason and location of the crash.
There's a big article about assertions in wiki.
Assert will inform you that something wrong happend, possibly error to be fixed. In debug mode it will break and show callstack that will help you with fixing bug. So its a good practice to use. I would actually use if() and assert, because in Release your asserts should be turned off:
void print_number(int* somePtr) {
assert(somePtr != NULL);
if (somePtr != NULL)
printf ("%d\n",*somePtr);
// else do something
}
in " // else do something " you might think of throwing exception or returning error code.
Listen If Your (if) statement becomes True or False so compiler go for the next instructions.
But in assert.h when your statement becomes false "Program Terminates immediately" with assertion message.
EXAMPLE :*
#include <assert.h> #include <stdio.h>
int main () {
int a;
printf("Enter an integer value: "); scanf("%d", &a); assert(a >= 10);
printf("Integer entered is %d\n", a);
return(0); }