/* Use pthread.h to run background tasks such as reminder
* aim is to avoid signals (per-process)
*/
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <glib.h>
#include "types.h"
#include "../core/context.h"
typedef struct BotTask {
pthread_t tid;
BotReminder *reminder;
} BotTask;
/* protected globals */
static BotCommand g_lastCommand = BOT_CMD_NONE;
static BotReminder *g_tmp_lastReminder = NULL;
/* store a thread per user */
static GHashTable* g_taskTable = NULL;
BotCommand bot_get_last_command()
{
return g_lastCommand;
}
void bot_set_last_command(const BotCommand cmd)
{
g_lastCommand = cmd;
}
BotReminder *bot_get_last_reminder()
{
BotReminder *reminder = malloc(sizeof *reminder);
if(g_tmp_lastReminder) {
/* deep copy, but we don't care about from */
reminder->minutes = g_tmp_lastReminder->minutes;
reminder->from = g_string_new("");
free_bot_reminder(g_tmp_lastReminder); /* can only be used once */
g_tmp_lastReminder = NULL;
}
return reminder;
}
void bot_set_last_reminder(const unsigned int minutes)
{
BotReminder *reminder;
if(g_tmp_lastReminder) {
fprintf(stderr, "WARNING: overwriting reminder. This should not happen.\n");
free_bot_reminder(g_tmp_lastReminder); /* can only be used once */
g_tmp_lastReminder = NULL;
}
reminder = malloc(sizeof *reminder);
reminder->minutes = minutes;
reminder->from = g_string_new(""); /* will be set afterwards */
g_tmp_lastReminder = reminder;
}
void free_bot_task (BotTask * data)
{
/* cancel and wait for the thread to finish */
pthread_cancel(data->tid);
pthread_join(data->tid, NULL);
if(data->reminder) {
free_bot_reminder(data->reminder);
}
free(data);
}
void bot_task_table_init()
{
if (g_taskTable) {
fprintf(stderr, "Programmer error: User table has already been initialized. Won't do it again.");
return;
}
g_taskTable = g_hash_table_new(g_str_hash, g_str_equal);
}
void bot_task_table_insert(gchar * const key, BotTask * const value)
{
BotTask * v;
if (!g_taskTable) {
bot_task_table_init();
}
if ((v = g_hash_table_lookup(g_taskTable, key))) { /* value already present */
free_bot_task(v);
}
g_hash_table_insert(g_taskTable, key, value);
}
void *bot_reminder_task(void *arg)
{
const BotReminder * const reminder = (const BotReminder * const)arg;
BotXmppCtx * bctx = bot_get_ctx();
GString *msg = g_string_new(NULL);
g_string_printf(msg, "OHI! Segna le spese! Te lo ricordo di nuovo tra %d minuti.\n", reminder->minutes);
while (1) {
sleep(reminder->minutes * 60); // only this thread waits
/* send a message */
bctx = bot_get_ctx(); // TODO needs a lock
send_message(bctx->conn, reminder->from, bctx->ctx, msg);
}
}
/* start a thread and update task table */
int bot_start_task (BotReminder *reminder)
{
pthread_t tid;
BotTask *task;
if ((task = g_hash_table_lookup(g_taskTable, reminder->from->str))) { /* value already present, stop reminder */
free_bot_task(task);
}
task = malloc(sizeof *task);
if(pthread_create(&tid, NULL, bot_reminder_task, reminder) != 0) {
fprintf(stderr, "Unable to start thread. No reminder will be set.");
return -1;
}
task->tid = tid;
task->reminder = reminder;
bot_task_table_insert(reminder->from->str, task);
return 0;
}
void bot_cancel_task(const gchar * const user)
{
BotTask *task;
if(g_taskTable) {
if ((task = g_hash_table_lookup(g_taskTable, user))) { /* value already present, stop reminder */
free_bot_task(task);
}
if(g_tmp_lastReminder) {
free_bot_reminder(g_tmp_lastReminder);
}
g_hash_table_remove(g_taskTable, user);
}
}