source: trunk/libtransmission/watchdir-generic.c

Last change on this file was 14651, checked in by mikedld, 5 years ago

#5663: Rework directory watching in daemon

Implement BSD/Darwin (kqueue) and Windows (ReadDirectoryChanges?) mechanisms
for receiving directory change notifications. Use events instead of polling
for changes. Retry file parsing up to 3 times before giving up.

Huge thanks to missionsix for preparing first two versions of the patch.

  • Property svn:keywords set to Date Rev Author Id
File size: 2.6 KB
Line 
1/*
2 * This file Copyright (C) 2015-2016 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 * $Id: watchdir-generic.c 14651 2016-01-02 14:28:59Z mikedld $
8 */
9
10#include <assert.h>
11#include <errno.h>
12
13#include <event2/event.h>
14
15#define __LIBTRANSMISSION_WATCHDIR_MODULE__
16
17#include "transmission.h"
18#include "log.h"
19#include "ptrarray.h"
20#include "utils.h"
21#include "watchdir.h"
22#include "watchdir-common.h"
23
24/***
25****
26***/
27
28#define log_error(...) (!tr_logLevelIsActive (TR_LOG_ERROR) ? (void) 0 : \
29  tr_logAddMessage (__FILE__, __LINE__, TR_LOG_ERROR, "watchdir:generic", __VA_ARGS__))
30
31/***
32****
33***/
34
35typedef struct tr_watchdir_generic
36{
37  tr_watchdir_backend   base;
38
39  struct event        * event;
40  tr_ptrArray           dir_entries;
41}
42tr_watchdir_generic;
43
44#define BACKEND_UPCAST(b) ((tr_watchdir_generic *) (b))
45
46/* Non-static and mutable for unit tests */
47struct timeval tr_watchdir_generic_interval = { 10, 0 };
48
49/***
50****
51***/
52
53static void
54tr_watchdir_generic_on_event (evutil_socket_t   fd UNUSED,
55                              short             type UNUSED,
56                              void            * context)
57{
58  const tr_watchdir_t handle = context;
59  tr_watchdir_generic * const backend = BACKEND_UPCAST (tr_watchdir_get_backend (handle));
60
61  tr_watchdir_scan (handle, &backend->dir_entries);
62}
63
64static void
65tr_watchdir_generic_free (tr_watchdir_backend * backend_base)
66{
67  tr_watchdir_generic * const backend = BACKEND_UPCAST (backend_base);
68
69  if (backend == NULL)
70    return;
71
72  assert (backend->base.free_func == &tr_watchdir_generic_free);
73
74  if (backend->event != NULL)
75    {
76      event_del (backend->event);
77      event_free (backend->event);
78    }
79
80  tr_ptrArrayDestruct (&backend->dir_entries, &tr_free);
81
82  tr_free (backend);
83}
84
85tr_watchdir_backend *
86tr_watchdir_generic_new (tr_watchdir_t handle)
87{
88  tr_watchdir_generic * backend;
89
90  backend = tr_new0 (tr_watchdir_generic, 1);
91  backend->base.free_func = &tr_watchdir_generic_free;
92
93  if ((backend->event = event_new (tr_watchdir_get_event_base (handle), -1, EV_PERSIST,
94                                   &tr_watchdir_generic_on_event, handle)) == NULL)
95    {
96      log_error ("Failed to create event: %s", tr_strerror (errno));
97      goto fail;
98    }
99
100  if (event_add (backend->event, &tr_watchdir_generic_interval) == -1)
101    {
102      log_error ("Failed to add event: %s", tr_strerror (errno));
103      goto fail;
104    }
105
106  /* Run initial scan on startup */
107  event_active (backend->event, EV_READ, 0);
108
109  return BACKEND_DOWNCAST (backend);
110
111fail:
112  tr_watchdir_generic_free (BACKEND_DOWNCAST (backend));
113  return NULL;
114}
Note: See TracBrowser for help on using the repository browser.