~hkdb/geary/disco-3.34.1

« back to all changes in this revision

Viewing changes to src/engine/util/util-synchronization.vala

  • Committer: hkdb
  • Date: 2019-10-08 10:54:21 UTC
  • Revision ID: hkdb@3df.io-20191008105421-3dkwnpnhcamm77to
Tags: upstream-3.34.1-disco
ImportĀ upstreamĀ versionĀ 3.34.1-disco

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright 2016 Software Freedom Conservancy Inc.
 
2
 *
 
3
 * This software is licensed under the GNU Lesser General Public License
 
4
 * (version 2.1 or later).  See the COPYING file in this distribution.
 
5
 */
 
6
 
 
7
namespace Geary.Synchronization {
 
8
 
 
9
/**
 
10
 * A synchronization primitive that spins waiting for a completion state to be reached.
 
11
 *
 
12
 * SpinWaiter allows for the caller to specify work to be performed periodically in a callback
 
13
 * while waiting for another thread to notify completion.
 
14
 */
 
15
 
 
16
public class SpinWaiter : BaseObject {
 
17
    public delegate bool PollService();
 
18
 
 
19
    private int poll_msec;
 
20
    private PollService cb;
 
21
    private Mutex mutex = Mutex();
 
22
    private Cond cond = Cond();
 
23
    private bool notified = false;
 
24
 
 
25
    /**
 
26
     * Create a {@link SpinWaiter}.
 
27
     *
 
28
     * poll_msec indicates how long to delay while spinning before interrupting and allowing
 
29
     * the {@link PollService} to execute.  If poll_msec is zero or less, PollService will be
 
30
     * called constantly.
 
31
     */
 
32
    public SpinWaiter(int poll_msec, owned PollService cb) {
 
33
        this.poll_msec = poll_msec;
 
34
        this.cb = (owned) cb;
 
35
    }
 
36
 
 
37
    /**
 
38
     * Spins waiting for a completion state to be reached.
 
39
     *
 
40
     * There's two ways the completion state can be reached: (1) PollService returns false,
 
41
     * indicating an abort state, (2) {@link notify} is called, indicating a success state, or
 
42
     * (3) the Cancellable was cancelled, causing an IOError.CANCELLED exception to be thrown.
 
43
     *
 
44
     * {@link PollService} will be called from within the calling thread context.
 
45
     *
 
46
     * Although this is thread-safe, it's not designed to be invoked by multiple callers.  That
 
47
     * could cause the PollService callback to be called more often than specified in the
 
48
     * constructor.
 
49
     */
 
50
    public bool wait(Cancellable? cancellable = null) throws Error {
 
51
        // normalize poll_msec; negative values are zeroed
 
52
        int64 actual_poll_msec = Numeric.int64_floor(0, poll_msec);
 
53
 
 
54
        bool result;
 
55
 
 
56
        mutex.lock();
 
57
 
 
58
        while (!notified) {
 
59
            if (cancellable != null && cancellable.is_cancelled())
 
60
                break;
 
61
 
 
62
            int64 end_time = get_monotonic_time() + (actual_poll_msec * TimeSpan.MILLISECOND);
 
63
            if (!cond.wait_until(mutex, end_time)) {
 
64
                // timeout passed, allow the callback to run
 
65
                mutex.unlock();
 
66
                if (!cb()) {
 
67
                    // PollService returned false, abort
 
68
                    mutex.lock();
 
69
 
 
70
                    break;
 
71
                }
 
72
                mutex.lock();
 
73
            }
 
74
        }
 
75
 
 
76
        result = notified;
 
77
 
 
78
        mutex.unlock();
 
79
 
 
80
        if (cancellable.is_cancelled())
 
81
            throw new IOError.CANCELLED("SpinWaiter.wait cancelled");
 
82
 
 
83
        return result;
 
84
    }
 
85
 
 
86
    /**
 
87
     * Signals a completion state to a thread calling {@link wait}.
 
88
     *
 
89
     * This call is thread-safe.  However, once a {@link SpinWaiter} has been signalled to stop,
 
90
     * it cannot be restarted.
 
91
     */
 
92
    public new void notify() {
 
93
        mutex.lock();
 
94
 
 
95
        notified = true;
 
96
        cond.broadcast();
 
97
 
 
98
        mutex.unlock();
 
99
    }
 
100
 
 
101
    /**
 
102
     * Indicates if the {@link SpinWaiter} has been notified.
 
103
     *
 
104
     * Other completion states (PollService returning false, Cancellable being cancelled in
 
105
     * {@link wait}) are not recorded here.
 
106
     *
 
107
     * This method is thread-safe.
 
108
     *
 
109
     * @see notify
 
110
     */
 
111
    public bool is_notified() {
 
112
        bool result;
 
113
 
 
114
        mutex.lock();
 
115
 
 
116
        result = notified;
 
117
 
 
118
        mutex.unlock();
 
119
 
 
120
        return result;
 
121
    }
 
122
}
 
123
 
 
124
}