~hkdb/geary/bionic

« back to all changes in this revision

Viewing changes to src/engine/db/db-transaction-async-job.vala

  • Committer: hkdb
  • Date: 2019-09-26 18:29:27 UTC
  • Revision ID: hkdb@3df.io-20190926182927-5371bd6xoib95isx
Tags: upstream-3.32.2-bionic
ImportĀ upstreamĀ versionĀ 3.32.2-bionic

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright 2016 Software Freedom Conservancy Inc.
 
3
 *
 
4
 * This software is licensed under the GNU Lesser General Public License
 
5
 * (version 2.1 or later).  See the COPYING file in this distribution.
 
6
 */
 
7
 
 
8
private class Geary.Db.TransactionAsyncJob : BaseObject {
 
9
 
 
10
    internal Connection? cx { get; private set; default = null; }
 
11
    internal Cancellable cancellable { get; private set; }
 
12
 
 
13
    private TransactionType type;
 
14
    private unowned TransactionMethod cb;
 
15
    private Nonblocking.Event completed;
 
16
    private TransactionOutcome outcome = TransactionOutcome.ROLLBACK;
 
17
    private Error? caught_err = null;
 
18
 
 
19
 
 
20
    public TransactionAsyncJob(Connection? cx,
 
21
                               TransactionType type,
 
22
                               TransactionMethod cb,
 
23
                               Cancellable? cancellable) {
 
24
        this.cx = cx;
 
25
        this.type = type;
 
26
        this.cb = cb;
 
27
        this.cancellable = cancellable ?? new Cancellable();
 
28
 
 
29
        this.completed = new Nonblocking.Event();
 
30
    }
 
31
 
 
32
    public bool is_cancelled() {
 
33
        return cancellable.is_cancelled();
 
34
    }
 
35
 
 
36
    // Called in background thread context
 
37
    internal void execute(Connection cx) {
 
38
        // execute transaction
 
39
        try {
 
40
            // possible was cancelled during interim of scheduling and execution
 
41
            if (is_cancelled())
 
42
                throw new IOError.CANCELLED("Async transaction cancelled");
 
43
 
 
44
            outcome = cx.exec_transaction(type, cb, cancellable);
 
45
        } catch (Error err) {
 
46
            if (!(err is IOError.CANCELLED))
 
47
                debug("AsyncJob: transaction completed with error: %s", err.message);
 
48
 
 
49
            caught_err = err;
 
50
        }
 
51
 
 
52
        schedule_completion();
 
53
    }
 
54
 
 
55
    // Called in background thread context
 
56
    internal void failed(Error err) {
 
57
        // store as a caught thread to report to original caller
 
58
        caught_err = err;
 
59
 
 
60
        schedule_completion();
 
61
    }
 
62
 
 
63
    private void schedule_completion() {
 
64
        // notify foreground thread of completion
 
65
        // because Idle doesn't hold a ref, manually keep this object alive
 
66
        ref();
 
67
 
 
68
        // NonblockingSemaphore and its brethren are not thread-safe, so need to signal notification
 
69
        // of completion in the main thread
 
70
        Idle.add(on_notify_completed);
 
71
    }
 
72
 
 
73
    private bool on_notify_completed() {
 
74
        try {
 
75
            completed.notify();
 
76
        } catch (Error err) {
 
77
            if (caught_err != null && !(caught_err is IOError.CANCELLED)) {
 
78
                debug("Unable to notify AsyncTransaction has completed w/ err %s: %s",
 
79
                    caught_err.message, err.message);
 
80
            } else {
 
81
                debug("Unable to notify AsyncTransaction has completed w/o err: %s", err.message);
 
82
            }
 
83
        }
 
84
 
 
85
        // manually unref; do NOT touch "this" once unref() returns, as this object may be freed
 
86
        unref();
 
87
 
 
88
        return false;
 
89
    }
 
90
 
 
91
    // No way to cancel this because the callback thread *must* finish before
 
92
    // we move on here.  Any I/O the thread is doing can still be cancelled
 
93
    // using the job's cancellable.
 
94
    public async TransactionOutcome wait_for_completion_async()
 
95
        throws Error {
 
96
        yield this.completed.wait_async();
 
97
        if (this.caught_err != null)
 
98
            throw this.caught_err;
 
99
 
 
100
        return this.outcome;
 
101
    }
 
102
}