-/* A Bison parser, made by GNU Bison 1.875c. */
+/* A Bison parser, made by GNU Bison 1.875. */
/* Skeleton parser for Yacc-like parsing with Bison,
- Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
enum _MERIDIAN Meridian;
} YYSTYPE;
/* Line 191 of yacc.c. */
-#line 227 "y.tab.c"
+#line 226 "y.tab.c"
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
# define YYSTYPE_IS_DECLARED 1
# define YYSTYPE_IS_TRIVIAL 1
/* Line 214 of yacc.c. */
-#line 239 "y.tab.c"
+#line 238 "y.tab.c"
#if ! defined (yyoverflow) || YYERROR_VERBOSE
-# ifndef YYFREE
-# define YYFREE free
-# endif
-# ifndef YYMALLOC
-# define YYMALLOC malloc
-# endif
-
/* The parser invokes alloca or malloc; define the necessary symbols. */
-# ifdef YYSTACK_USE_ALLOCA
-# if YYSTACK_USE_ALLOCA
-# define YYSTACK_ALLOC alloca
-# endif
+# if YYSTACK_USE_ALLOCA
+# define YYSTACK_ALLOC alloca
# else
-# if defined (alloca) || defined (_ALLOCA_H)
-# define YYSTACK_ALLOC alloca
-# else
-# ifdef __GNUC__
-# define YYSTACK_ALLOC __builtin_alloca
+# ifndef YYSTACK_USE_ALLOCA
+# if defined (alloca) || defined (_ALLOCA_H)
+# define YYSTACK_ALLOC alloca
+# else
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# endif
# endif
# endif
# endif
# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
# define YYSIZE_T size_t
# endif
-# define YYSTACK_ALLOC YYMALLOC
-# define YYSTACK_FREE YYFREE
+# define YYSTACK_ALLOC malloc
+# define YYSTACK_FREE free
# endif
#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */
#if (! defined (yyoverflow) \
&& (! defined (__cplusplus) \
- || (defined (YYSTYPE_IS_TRIVIAL) && YYSTYPE_IS_TRIVIAL)))
+ || (YYSTYPE_IS_TRIVIAL)))
/* A type that is properly aligned for any stack member. */
union yyalloc
/* Copy COUNT objects from FROM to TO. The source and destination do
not overlap. */
# ifndef YYCOPY
-# if defined (__GNUC__) && 1 < __GNUC__
+# if 1 < __GNUC__
# define YYCOPY(To, From, Count) \
__builtin_memcpy (To, From, (Count) * sizeof (*(From)))
# else
First, the terminals, then, starting at YYNTOKENS, nonterminals. */
static const char *const yytname[] =
{
- "$end", "error", "$undefined", "tDAY", "tDAYZONE", "tMERIDIAN",
- "tMONTH", "tMONTH_UNIT", "tSEC_UNIT", "tSNUMBER", "tUNUMBER", "tZONE",
- "':'", "'/'", "','", "$accept", "spec", "item", "time", "zone",
- "numzone", "date", "rel", "o_merid", 0
+ "$end", "error", "$undefined", "tDAY", "tDAYZONE", "tMERIDIAN", "tMONTH",
+ "tMONTH_UNIT", "tSEC_UNIT", "tSNUMBER", "tUNUMBER", "tZONE", "':'",
+ "'/'", "','", "$accept", "spec", "item", "time", "zone", "numzone",
+ "date", "rel", "o_merid", 0
};
#endif
#define YYACCEPT goto yyacceptlab
#define YYABORT goto yyabortlab
-#define YYERROR goto yyerrorlab
-
+#define YYERROR goto yyerrlab1
/* Like YYERROR except do call yyerror. This remains here temporarily
to ease the transition to the new meaning of YYERROR, for GCC.
are run). */
#ifndef YYLLOC_DEFAULT
-# define YYLLOC_DEFAULT(Current, Rhs, N) \
- ((Current).first_line = (Rhs)[1].first_line, \
- (Current).first_column = (Rhs)[1].first_column, \
- (Current).last_line = (Rhs)[N].last_line, \
- (Current).last_column = (Rhs)[N].last_column)
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ Current.first_line = Rhs[1].first_line; \
+ Current.first_column = Rhs[1].first_column; \
+ Current.last_line = Rhs[N].last_line; \
+ Current.last_column = Rhs[N].last_column;
#endif
/* YYLEX -- calling `yylex' with the right arguments. */
/*------------------------------------------------------------------.
| yy_stack_print -- Print the state stack from its BOTTOM up to its |
-| TOP (included). |
+| TOP (cinluded). |
`------------------------------------------------------------------*/
#if defined (__STDC__) || defined (__cplusplus)
#endif
{
int yyi;
- unsigned int yylno = yyrline[yyrule];
+ unsigned int yylineno = yyrline[yyrule];
YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ",
- yyrule - 1, yylno);
+ yyrule - 1, yylineno);
/* Print the symbols being reduced, and their result. */
for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++)
YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]);
SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH)
evaluated with infinite-precision integer arithmetic. */
-#if defined (YYMAXDEPTH) && YYMAXDEPTH == 0
+#if YYMAXDEPTH == 0
# undef YYMAXDEPTH
#endif
}
-/* Line 1000 of yacc.c. */
-#line 1414 "y.tab.c"
+/* Line 991 of yacc.c. */
+#line 1405 "y.tab.c"
\f
yyvsp -= yylen;
yyssp -= yylen;
{
YYSIZE_T yysize = 0;
int yytype = YYTRANSLATE (yychar);
- const char* yyprefix;
char *yymsg;
- int yyx;
+ int yyx, yycount;
+ yycount = 0;
/* Start YYX at -YYN if negative to avoid negative indexes in
YYCHECK. */
- int yyxbegin = yyn < 0 ? -yyn : 0;
-
- /* Stay within bounds of both yycheck and yytname. */
- int yychecklim = YYLAST - yyn;
- int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
- int yycount = 0;
-
- yyprefix = ", expecting ";
- for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ for (yyx = yyn < 0 ? -yyn : 0;
+ yyx < (int) (sizeof (yytname) / sizeof (char *)); yyx++)
if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
- {
- yysize += yystrlen (yyprefix) + yystrlen (yytname [yyx]);
- yycount += 1;
- if (yycount == 5)
- {
- yysize = 0;
- break;
- }
- }
- yysize += (sizeof ("syntax error, unexpected ")
- + yystrlen (yytname[yytype]));
+ yysize += yystrlen (yytname[yyx]) + 15, yycount++;
+ yysize += yystrlen ("syntax error, unexpected ") + 1;
+ yysize += yystrlen (yytname[yytype]);
yymsg = (char *) YYSTACK_ALLOC (yysize);
if (yymsg != 0)
{
if (yycount < 5)
{
- yyprefix = ", expecting ";
- for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ yycount = 0;
+ for (yyx = yyn < 0 ? -yyn : 0;
+ yyx < (int) (sizeof (yytname) / sizeof (char *));
+ yyx++)
if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
{
- yyp = yystpcpy (yyp, yyprefix);
+ const char *yyq = ! yycount ? ", expecting " : " or ";
+ yyp = yystpcpy (yyp, yyq);
yyp = yystpcpy (yyp, yytname[yyx]);
- yyprefix = " or ";
+ yycount++;
}
}
yyerror (yymsg);
/* If just tried and failed to reuse lookahead token after an
error, discard it. */
- if (yychar <= YYEOF)
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
{
- /* If at end of input, pop the error token,
- then the rest of the stack, then return failure. */
- if (yychar == YYEOF)
- for (;;)
- {
- YYPOPSTACK;
- if (yyssp == yyss)
- YYABORT;
- YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
- yydestruct (yystos[*yyssp], yyvsp);
- }
+ /* Pop the error token. */
+ YYPOPSTACK;
+ /* Pop the rest of the stack. */
+ while (yyss < yyssp)
+ {
+ YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
+ yydestruct (yystos[*yyssp], yyvsp);
+ YYPOPSTACK;
+ }
+ YYABORT;
}
- else
- {
- YYDSYMPRINTF ("Error: discarding", yytoken, &yylval, &yylloc);
- yydestruct (yytoken, &yylval);
- yychar = YYEMPTY;
- }
+ YYDSYMPRINTF ("Error: discarding", yytoken, &yylval, &yylloc);
+ yydestruct (yytoken, &yylval);
+ yychar = YYEMPTY;
+
}
/* Else will try to reuse lookahead token after shifting the error
token. */
- goto yyerrlab1;
+ goto yyerrlab2;
-/*---------------------------------------------------.
-| yyerrorlab -- error raised explicitly by YYERROR. |
-`---------------------------------------------------*/
-yyerrorlab:
+/*----------------------------------------------------.
+| yyerrlab1 -- error raised explicitly by an action. |
+`----------------------------------------------------*/
+yyerrlab1:
-#ifdef __GNUC__
- /* Pacify GCC when the user code never invokes YYERROR and the label
- yyerrorlab therefore never appears in user code. */
- if (0)
- goto yyerrorlab;
+ /* Suppress GCC warning that yyerrlab1 is unused when no action
+ invokes YYERROR. */
+#if defined (__GNUC_MINOR__) && 2093 <= (__GNUC__ * 1000 + __GNUC_MINOR__) \
+ && !defined __cplusplus
+ __attribute__ ((__unused__))
#endif
- yyvsp -= yylen;
- yyssp -= yylen;
- yystate = *yyssp;
- goto yyerrlab1;
+ goto yyerrlab2;
-/*-------------------------------------------------------------.
-| yyerrlab1 -- common code for both syntax error and YYERROR. |
-`-------------------------------------------------------------*/
-yyerrlab1:
+
+/*---------------------------------------------------------------.
+| yyerrlab2 -- pop states until the error token can be shifted. |
+`---------------------------------------------------------------*/
+yyerrlab2:
yyerrstatus = 3; /* Each real token shifted decrements this. */
for (;;)
YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
yydestruct (yystos[yystate], yyvsp);
- YYPOPSTACK;
- yystate = *yyssp;
+ yyvsp--;
+ yystate = *--yyssp;
+
YY_STACK_PRINT (yyss, yyssp);
}
#ifdef HAVE_OPENSSL
destruct_ssl();
#endif
- serv_calendar_destroy();
+ serv_calendar_destroy(); // FIXME: Shouldn't be here, should be by a cleanup hook surely.
CtdlDestroyProtoHooks();
CtdlDestroyDeleteHooks();
CtdlDestroyXmsgHooks();
}
}
+
+
+/*
+ * New thread interface.
+ * To create a thread you must call one of the create thread functions.
+ * You must pass it the address of (a pointer to a CtdlThreadNode initialised to NULL) like this
+ * struct CtdlThreadNode *node = NULL;
+ * pass in &node
+ * If the thread is created *node will point to the thread control structure for the created thread.
+ * If the thread creation fails *node remains NULL
+ * Do not free the memory pointed to by *node, it doesn't belong to you.
+ * If your thread function returns it will be started again without creating a new thread.
+ * If your thread function wants to exit it should call CtdlThreadExit(ret_code);
+ * This new interface duplicates much of the eCrash stuff. We should go for closer integration since that would
+ * remove the need for the calls to eCrashRegisterThread and friends
+ */
+
+// FIXME: these defines should be else where
+#define CTDLTHREAD_BIGSTACK 0x0001
+
+struct CtdlThreadNode *CtdlThreadList = NULL;
+static pthread_mutex_t ThreadWaiterMutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t ThreadWaiterCond = PTHREAD_COND_INITIALIZER;
+
+
+/*
+ * So we now have a sleep command that works with threads but it is in seconds
+ */
+void CtdlThreadSleep(int secs)
+{
+
+ struct timespec wake_time;
+ struct timeval time_now;
+
+
+ memset (&wake_time, 0, sizeof(struct timespec));
+ gettimeofday(&time_now, NULL);
+ wake_time.tv_sec = time_now.tv_sec + secs;
+ pthread_cond_timedwait(&ThreadWaiterCond, &ThreadWaiterMutex, &wake_time);
+}
+
+
+/*
+ * Routine to clean up our thread function on exit
+ */
+void ctdl_internal_thread_cleanup(void *arg)
+{
+ struct CtdlThreadNode *this_thread;
+ // arg is a pointer to our thread structure
+ this_thread = (struct CtdlThreadNode *) arg;
+ if (this_thread->valid)
+ {
+ /*
+ * In here we were called by the current thread because it is exiting
+ * NB. WE ARE THE CURRENT THREAD
+ */
+ CtdlLogPrintf(CTDL_NOTICE, "Thread \"%s\" (%ld) exited.\n", this_thread->name, this_thread->tid);
+ this_thread->running = FALSE;
+ this_thread->valid = FALSE; // needs to be last thing else house keeping will unlink us too early
+ /*
+ * Our thread is exiting either because it wanted to end or because the server is stopping
+ * We need to clean up
+ */
+ #ifdef HAVE_BACKTRACE
+ eCrash_UnregisterThread();
+ #endif
+ }
+ else
+ {
+ if (this_thread->tid == pthread_self())
+ {
+ CtdlLogPrintf(CTDL_EMERG, "Thread system PANIC a thread is trying to clean up after itself.\n");
+ time_to_die = -1;
+ return;
+ }
+ /*
+ * In here we were called by some other thread that wants to clean up any dead threads
+ * NB. WE ARE NOT THE THREAD BEING CLEANED
+ */
+ // We probably got called by house keeping or master shutdown so we unlink the dead threads here
+ num_threads--;
+
+ begin_critical_section(S_THREAD_LIST);
+ if(this_thread->name)
+ free(this_thread->name);
+ if(this_thread->prev)
+ this_thread->prev->next = this_thread->next;
+ if(this_thread->next)
+ this_thread->next->prev = this_thread->next;
+ end_critical_section(S_THREAD_LIST);
+ free(this_thread);
+ }
+}
+
+
+/*
+ * Garbage collection routine.
+ * Gets called by do_housekeeping() and in master_cleanup() to clean up the thread list
+ */
+void ctdl_internal_thread_gc (int shutdown)
+{
+ struct CtdlThreadNode *this_thread, *that_thread;
+
+ this_thread = CtdlThreadList;
+ while(this_thread)
+ {
+ that_thread = this_thread;
+ this_thread = this_thread->next;
+
+ if(shutdown && that_thread->valid)
+ { // We want the threads to shutdown so first ask it nicely
+ that_thread->running = FALSE;
+ // Wait for it to exit
+ CtdlThreadSleep(1);
+
+ if(that_thread->valid) // Be more brutal about it
+ pthread_cancel (that_thread->tid);
+ // Wait for it to exit
+ CtdlThreadSleep(1);
+ }
+
+ if (that_thread->valid == FALSE)
+ {
+ CtdlLogPrintf(CTDL_NOTICE, "Joining thread \"%s\" (%ld)\n", that_thread->name, that_thread->tid);
+ pthread_join(that_thread->tid, NULL);
+ ctdl_internal_thread_cleanup(that_thread);
+ }
+ }
+}
+
+/*
+ * Runtime function for a Citadel Thread.
+ * This initialises the threads environment and then calls the user supplied thread function
+ * Note that this is the REAL thread function and wraps the users thread function.
+ */
+void *ctdl_internal_thread_func (void *arg)
+{
+ struct CtdlThreadNode *this_thread;
+ void *ret = NULL;
+
+ // Get our thread data structure
+ this_thread = (struct CtdlThreadNode *) arg;
+ // Tell the world we are here
+ CtdlLogPrintf(CTDL_NOTICE, "Spawned a new thread \"%s\" (%ld). \n", this_thread->name, this_thread->tid);
+
+ num_threads++; // Increase the count of threads in the system.
+
+ // Register for tracing
+ #ifdef HAVE_BACKTRACE
+ eCrash_RegisterThread(this_thread->name, 0);
+ #endif
+
+ // Register the cleanup function to take care of when we exit.
+ pthread_cleanup_push(ctdl_internal_thread_cleanup, arg);
+
+ this_thread->running = TRUE;
+
+ while ((!time_to_die) && (this_thread->running))
+ { // Call the users thread function
+ ret = (this_thread->thread_func)(this_thread->user_args);
+ }
+
+ /*
+ * Our thread is exiting either because it wanted to end or because the server is stopping
+ * We need to clean up
+ */
+ #ifdef HAVE_BACKTRACE
+ eCrash_UnregisterThread();
+ #endif
+
+ pthread_cleanup_pop(1); // Execute our cleanup routine and remove it
+
+ return(ret);
+}
+
+
+
+/*
+ * Internal function to create a thread.
+ * Must be called from within a S_THREAD_LIST critical section
+ */
+int ctdl_internal_create_thread(char *name, int flags, void *(*thread_func) (void *arg), void *arg, struct CtdlThreadNode **new_thread)
+{
+ int ret = 0;
+ pthread_attr_t attr;
+ struct CtdlThreadNode *this_thread;
+
+ if (*new_thread)
+ {
+ lprintf(CTDL_EMERG, "Possible attempt to overwrite an existing thread!!!\n");
+ return -1;
+ }
+
+ this_thread = malloc(sizeof(struct CtdlThreadNode));
+ if (this_thread == NULL) {
+ lprintf(CTDL_EMERG, "can't allocate CtdlThreadNode, exiting\n");
+ return ret;
+ }
+ // Ensuring this is zero'd means we make sure the thread doesn't start doing its thing until we are ready.
+ memset (this_thread, 0, sizeof(struct CtdlThreadNode));
+
+ if ((ret = pthread_attr_init(&attr))) {
+ lprintf(CTDL_EMERG, "pthread_attr_init: %s\n", strerror(ret));
+ free(this_thread);
+ return ret;
+ }
+
+ /* Our per-thread stacks need to be bigger than the default size,
+ * otherwise the MIME parser crashes on FreeBSD, and the IMAP service
+ * crashes on 64-bit Linux.
+ */
+ if (flags & CTDLTHREAD_BIGSTACK)
+ {
+ if ((ret = pthread_attr_setstacksize(&attr, THREADSTACKSIZE))) {
+ lprintf(CTDL_EMERG, "pthread_attr_setstacksize: %s\n",
+ strerror(ret));
+ pthread_attr_destroy(&attr);
+ free(this_thread);
+ return ret;
+ }
+ }
+
+ /*
+ * If we got here we are going to create the thread so we must initilise the structure
+ * first because most implimentations of threading can't create it in a stopped state
+ * and it might want to do things with its structure that aren't initialised otherwise.
+ */
+ if(name)
+ {
+ this_thread->name = strdup(name);
+ }
+ else
+ {
+ this_thread->name = strdup("Unknown Thread");
+ }
+ this_thread->flags = flags;
+ this_thread->thread_func = thread_func;
+ this_thread->user_args = arg;
+ this_thread->valid = 1; // Need this to prevent house keeping unlinking us from the list
+ /*
+ * We pass this_thread into the thread as its args so that it can find out information
+ * about itself and it has a bit of storage space for itself, not to mention that the REAL
+ * thread function needs to finish off the setup of the structure
+ */
+ if ((ret = pthread_create(&this_thread->tid, &attr, ctdl_internal_thread_func, this_thread) != 0))
+ {
+
+ lprintf(CTDL_ALERT, "Can't create thread: %s\n",
+ strerror(ret));
+ if (this_thread->name)
+ free (this_thread->name);
+ free(this_thread);
+ pthread_attr_destroy(&attr);
+ return ret;
+ }
+
+ this_thread->next = CtdlThreadList;
+ CtdlThreadList = this_thread;
+ *new_thread = this_thread;
+ pthread_attr_destroy(&attr);
+ return 0;
+}
+
+/*
+ * Wrapper function to create a thread
+ * ensures the critical section and other protections are in place.
+ * char *name = name to give to thread, if NULL, use generic name
+ * int flags = flags to determine type of thread and standard facilities
+ */
+int CtdlCreateThread(char *name, int flags, void *(*thread_func) (void *arg), void *arg, struct CtdlThreadNode **new_thread)
+{
+ int ret;
+
+ begin_critical_section(S_THREAD_LIST);
+ ret = ctdl_internal_create_thread(name, flags, thread_func, arg, new_thread);
+ end_critical_section(S_THREAD_LIST);
+ return ret;
+}
+
+
+
+/*
+ * Old thread interface.
+ */
+
+
struct worker_node *worker_list = NULL;
if ((ret = pthread_attr_init(&attr))) {
lprintf(CTDL_EMERG, "pthread_attr_init: %s\n", strerror(ret));
time_to_die = -1;
+ free(n);
return;
}
strerror(ret));
time_to_die = -1;
pthread_attr_destroy(&attr);
+ free(n);
return;
}
lprintf(CTDL_ALERT, "Can't create worker thread: %s\n",
strerror(ret));
+ time_to_die = -1;
+ pthread_attr_destroy(&attr);
+ free(n);
+ return;
}
n->next = worker_list;