#ifndef OBSERVER_H
#define OBSERVER_H
struct observer;
/* normal_stop notifications. */
typedef void (observer_normal_stop_ftype) (void);
extern struct observer *
observer_attach_normal_stop (observer_normal_stop_ftype *f);
extern void observer_detach_normal_stop (struct observer *observer);
extern void observer_notify_normal_stop (void);
#endif /* OBSERVER_H */
#include "defs.h"
#include "observer.h"
typedef void (generic_observer_notification_ftype) (const void *data,
const void *args);
struct observer
{
generic_observer_notification_ftype *notify;
void *data; /* No memory management needed for this field for now. */
};
struct observer_list
{
struct observer_list *next;
struct observer *observer;
};
static struct observer_list *
xalloc_observer_list_node (void)
{
struct observer_list *node = XMALLOC (struct observer_list);
node->observer = XMALLOC (struct observer);
return node;
}
static void
xfree_observer_list_node (struct observer_list *node)
{
xfree (node->observer);
xfree (node);
}
static struct observer *
generic_observer_attach (struct observer_list **list,
generic_observer_notification_ftype *notify,
void *data)
{
struct observer_list *observer_list = xalloc_observer_list_node ();
observer_list->next = *list;
observer_list->observer->notify = notify;
observer_list->observer->data = data;
*list = observer_list;
return observer_list->observer;
}
static void
generic_observer_detach (struct observer_list **list,
const struct observer *observer)
{
struct observer_list *previous_node = NULL;
struct observer_list *current_node = *list;
while (current_node != NULL)
{
if (current_node->observer == observer)
{
if (previous_node != NULL)
previous_node->next = current_node->next;
else
*list = current_node->next;
xfree_observer_list_node (current_node);
return;
}
previous_node = current_node;
current_node = current_node->next;
}
/* We should never reach this point. However, this should not be
a very serious error, so simply report a warning to the user. */
warning ("Failed to detach observer");
}
static void
generic_observer_notify (struct observer_list *list, const void *args)
{
struct observer_list *current_node = list;
while (current_node != NULL)
{
(*current_node->observer->notify) (current_node->observer->data, args);
current_node = current_node->next;
}
}
/* normal_stop notifications. */
static struct observer_list *normal_stop_observers = NULL;
void
observer_normal_stop_notification_stub (const void *data,
const void *unused_args)
{
observer_normal_stop_ftype *notify = (observer_normal_stop_ftype *) data;
(*notify) ();
}
struct observer *
observer_attach_normal_stop (observer_normal_stop_ftype *f)
{
return generic_observer_attach (&normal_stop_observers,
&observer_normal_stop_notification_stub,
(void *) f);
}
void
observer_detach_normal_stop (struct observer *observer)
{
generic_observer_detach (&normal_stop_observers, observer);
}
void
observer_notify_normal_stop (void)
{
generic_observer_notify (normal_stop_observers, NULL);
}
#include <stdio.h>
#include "observer.h"
#include <stdlib.h>
void *xmalloc (size_t size) { return malloc (size); }
void xfree (void *ptr) { free (ptr); }
void warning (const char *str, ...) { printf ("warning: %s.\n", str); }
int first_observer = 0;
int second_observer = 0;
int third_observer = 0;
void
reset_counters (void)
{
first_observer = 0;
second_observer = 0;
third_observer = 0;
}
void
check_counters (int first, int second, int third)
{
if (first_observer != first)
printf ("ERROR: first observer incorrect count: %d (expected %d).\n",
first_observer, first);
if (second_observer != second)
printf ("ERROR: second observer incorrect count: %d (expected %d).\n",
second_observer, second);
if (third_observer != third)
printf ("ERROR: third observer incorrect count: %d (expected %d).\n",
third_observer, third);
}
void
test_notifications (int first, int second, int third, char *msg)
{
printf ("-- Sending notification (%s)...\n", msg);
reset_counters ();
observer_notify_normal_stop ();
check_counters (first, second, third);
}
void
first_observer_notification (void)
{
first_observer++;
}
void
second_observer_notification (void)
{
second_observer++;
}
void
third_observer_notification (void)
{
third_observer++;
}
void
general_observer_test (void)
{
struct observer *first_obs = NULL;
struct observer *second_obs = NULL;
struct observer *third_obs = NULL;
/* First, try sending a notification without any observers attached. */
test_notifications (0, 0, 0, "no observer");
/* Now, attach one observer, and send a notification. */
second_obs = observer_attach_normal_stop (&second_observer_notification);
test_notifications (0, 1, 0, "one observer");
/* Remove the observer, and send a notification. */
observer_detach_normal_stop (second_obs);
test_notifications (0, 0, 0, "no observer");
/* With a new observer. */
first_obs = observer_attach_normal_stop (&first_observer_notification);
test_notifications (1, 0, 0, "a new observer");
/* With 2 observers. */
second_obs = observer_attach_normal_stop (&second_observer_notification);
test_notifications (1, 1, 0, "2 observers");
/* With 3 observers. */
third_obs = observer_attach_normal_stop (&third_observer_notification);
test_notifications (1, 1, 1, "3 observers");
/* Remove the middle observer. */
observer_detach_normal_stop (second_obs);
test_notifications (1, 0, 1, "middle observer removed");
/* Remove first observer. */
observer_detach_normal_stop (first_obs);
test_notifications (0, 0, 1, "first observer removed");
/* Remove last observer. */
observer_detach_normal_stop (third_obs);
test_notifications (0, 0, 0, "last observer removed");
/* Go back to 3 observers, and remove them in a different order... */
first_obs = observer_attach_normal_stop (&first_observer_notification);
second_obs = observer_attach_normal_stop (&second_observer_notification);
third_obs = observer_attach_normal_stop (&third_observer_notification);
test_notifications (1, 1, 1, "3 observers");
/* Remove the third observer... */
observer_detach_normal_stop (third_obs);
test_notifications (1, 1, 0, "third observer removed");
/* Remove the second observer... */
observer_detach_normal_stop (second_obs);
test_notifications (1, 0, 0, "second observer removed");
/* Remove the first observer... No more observers. */
observer_detach_normal_stop (first_obs);
test_notifications (0, 0, 0, "first observer removed - no more observers");
}
int
main (void)
{
general_observer_test ();
return 0;
}