Skip to content

Commit

Permalink
improved incoming call cancellation & refactored to use deferred events.
Browse files Browse the repository at this point in the history
This patch implements the concept of deferred events in order to avoid
an incoming event conflicting with a currently executing request, the
feature is currently only used for incoming calls but is a general
purpose mechanism in this regard.

Fixes gammu#376
  • Loading branch information
kstuart committed Apr 8, 2019
1 parent 4d2e7fc commit e08ba3b
Show file tree
Hide file tree
Showing 9 changed files with 593 additions and 22 deletions.
8 changes: 8 additions & 0 deletions include/gammu-limits.h
Expand Up @@ -16,6 +16,14 @@ extern "C" {
* Limits definitions.
*/


/**
* Maximum number of deferred events in queue.
*
* \ingroup Limits
*/
#define MAX_DEFERRED_EVENTS 5

/**
* Maximal length of manufacturer name.
*
Expand Down
1 change: 1 addition & 0 deletions libgammu/gsmreply.h
Expand Up @@ -198,6 +198,7 @@ typedef enum {
ID_SetPower,

ID_IncomingFrame,
ID_CheckCHUP,

ID_User1,
ID_User2,
Expand Down
107 changes: 106 additions & 1 deletion libgammu/gsmstate.c
@@ -1,11 +1,12 @@
/* (c) 2002-2005 by Marcin Wiacek and Michal Cihar */
/* (c) 2002-2005 by Marcin Wiacek and Michal Cihar */
/* Phones ID (c) partially by Walek */

#include <stdarg.h>
#define _GNU_SOURCE /* For strcasestr */
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <assert.h>

#include <gammu-call.h>
#include <gammu-settings.h>
Expand Down Expand Up @@ -1130,6 +1131,109 @@ static GSM_Error CheckReplyFunctions(GSM_StateMachine *s, GSM_Reply_Function *Re
}
}

GSM_Error EventQueue_Push(GSM_StateMachine *s, const EventBinding *binding)
{
DeferredEventQueue *Queue = &s->Phone.Data.DeferredEvents;

assert(binding != NULL);
assert(Queue->head < MAX_DEFERRED_EVENTS);

if(Queue->entries == MAX_DEFERRED_EVENTS)
return ERR_FULL;

Queue->event_bindings[Queue->head] = *binding;
Queue->head = (Queue->head + 1) % MAX_DEFERRED_EVENTS;
++Queue->entries;

assert(Queue->entries <= MAX_DEFERRED_EVENTS);

return ERR_NONE;
}

GSM_Error EventQueue_Pop(GSM_StateMachine *s, EventBinding *binding)
{
DeferredEventQueue *Queue = &s->Phone.Data.DeferredEvents;

assert(binding != NULL);

if(Queue->entries == 0)
return ERR_EMPTY;

*binding = Queue->event_bindings[Queue->tail];
Queue->tail = (Queue->tail + 1) % MAX_DEFERRED_EVENTS;
--Queue->entries;

assert(Queue->entries >= 0);

return ERR_NONE;
}

void GSM_CancelEventsOfType(GSM_StateMachine *s, unsigned event_types)
{
DeferredEventQueue *q = &s->Phone.Data.DeferredEvents;
int i = q->tail;

while(i != q->head) {
if(q->event_bindings[i].type & event_types)
q->event_bindings[i].event_cancelled = TRUE;

i = (i + 1) % MAX_DEFERRED_EVENTS;
}
}

GSM_Error GSM_DeferIncomingCallEvent(GSM_StateMachine *s, GSM_Call *call, BeforeDeferredEvent before_event)
{
GSM_Error error;
EventBinding binding;

if(s->Phone.Data.RequestID == ID_None) {
s->User.IncomingCall(s, call, s->User.IncomingCallUserData);
return ERR_NONE;
}

binding.type = GSM_EV_CALL;
binding.handler = (EventHandler)s->User.IncomingCall;
binding.before_event = before_event;
binding.after_event = NULL;
binding.event_cancelled = FALSE;
binding.event_data.call = *call;
binding.user_data = s->User.IncomingCallUserData;

error = EventQueue_Push(s, &binding);

if(error != ERR_NONE)
smprintf_level(s, D_ERROR,
"the incoming call handler could not be deferred.\n");

return error;
}

GSM_Error ProcessDeferredEvent(GSM_StateMachine *s)
{
EventBinding binding;
GSM_Error error = EventQueue_Pop(s, &binding);

if(error != ERR_NONE)
return error;

assert(s->Phone.Data.RequestID == ID_None);
assert(binding.handler != NULL);
assert(binding.type != GSM_EV_UNSET);

if(binding.event_cancelled == FALSE) {
if (binding.before_event)
error = binding.before_event(s);

if(error == ERR_NONE)
binding.handler(s, &binding.event_data, binding.user_data);
}

if(binding.after_event)
binding.after_event(s, &binding);

return error;
}

GSM_Error GSM_DispatchMessage(GSM_StateMachine *s)
{
GSM_Error error = ERR_UNKNOWNFRAME;
Expand Down Expand Up @@ -1161,6 +1265,7 @@ GSM_Error GSM_DispatchMessage(GSM_StateMachine *s)
error = ERR_NONE;
} else {
Phone->RequestID = ID_None;
while(ProcessDeferredEvent(s) == ERR_NONE);
}
}
}
Expand Down
68 changes: 68 additions & 0 deletions libgammu/gsmstate.h
Expand Up @@ -219,6 +219,70 @@ typedef struct _GSM_User GSM_User;
#include "debug.h"
#include "gsmreply.h"

typedef struct EventBinding_ EventBinding;

/**
* Generic event handler.
*/
typedef void (*EventHandler)(GSM_StateMachine * s, void *event_data,
void *user_data);

/**
* Customization point for any needed setup before an event handler is called,
* if anything other than ERR_NONE is returned the event handler won't be called.
*/
typedef GSM_Error (*BeforeDeferredEvent)(GSM_StateMachine *s);
/**
* Customization point for any needed cleanup after an event handler is called
* or cancelled.
*/
typedef void (*AfterDeferredEvent)(GSM_StateMachine *s, EventBinding *binding);

/**
* Identifies the event types.
*/
typedef enum {
GSM_EV_UNSET = 0,
GSM_EV_CALL = 0x00000001,
GSM_EV_ALL = 0xFFFFFFFF
} EventType;

/**
* An event binding associates event data with a handler.
*/
typedef struct EventBinding_ {
EventType type;
EventHandler handler;
BeforeDeferredEvent before_event;
AfterDeferredEvent after_event;
gboolean event_cancelled;
union {
GSM_Call call;
} event_data;
void *user_data;
} EventBinding;

/**
* Queue of events awaiting processing.
*/
typedef struct {
int head;
int tail;
int entries;
EventBinding event_bindings[MAX_DEFERRED_EVENTS];
} DeferredEventQueue;

/**
* Defers running an incoming call handler if an existing command is already
* executing, otherwise the handler is run immediately.
*/
GSM_Error GSM_DeferIncomingCallEvent(GSM_StateMachine *s, GSM_Call *call,
BeforeDeferredEvent before_event);

/**
* Cancels running any deferred event handlers matching the given event_types mask.
*/
void GSM_CancelEventsOfType(GSM_StateMachine *s, unsigned event_types);

/* ------------------------- Device layer ---------------------------------- */

Expand Down Expand Up @@ -687,6 +751,10 @@ typedef struct {
* Error returned by function in phone module.
*/
GSM_Error DispatchError;
/**
* Queue of events awaiting processing.
*/
DeferredEventQueue DeferredEvents;

/**
* Structure with private phone modules data.
Expand Down

0 comments on commit e08ba3b

Please sign in to comment.