~unity-api-team/unity-scopes-api/child-scopes-option

« back to all changes in this revision

Viewing changes to demo/scope-D.cpp

  • Committer: Michi Henning
  • Date: 2013-11-19 02:51:46 UTC
  • mto: This revision was merged to the branch mainline in revision 62.
  • Revision ID: michi.henning@canonical.com-20131119025146-kglcalpphl4ozzk7
Fixed scoperegistry to use new scoperunner and to figure out which scopes to run from config files.
Still to do:
- deal with overrides and OEM scopes, particularly the grouping aspect.
- SignalThread needs to invoke a callback for clean shut-down on receipt of SIGINT.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright (C) 2013 Canonical Ltd
3
 
 *
4
 
 * This program is free software: you can redistribute it and/or modify
5
 
 * it under the terms of the GNU General Public License version 3 as
6
 
 * published by the Free Software Foundation.
7
 
 *
8
 
 * This program is distributed in the hope that it will be useful,
9
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 
 * GNU General Public License for more details.
12
 
 *
13
 
 * You should have received a copy of the GNU General Public License
14
 
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
 
 *
16
 
 * Authored by: Michi Henning <michi.henning@canonical.com>
17
 
 */
18
 
 
19
 
#include <scopes/RegistryProxyFwd.h>
20
 
#include <scopes/Reply.h>
21
 
#include <scopes/Category.h>
22
 
#include <scopes/ResultItem.h>
23
 
#include <scopes/ScopeBase.h>
24
 
 
25
 
#include <algorithm>
26
 
#include <atomic>
27
 
#include <condition_variable>
28
 
#include <iostream>
29
 
#include <list>
30
 
#include <mutex>
31
 
 
32
 
#include <unistd.h>
33
 
 
34
 
#define EXPORT __attribute__ ((visibility ("default")))
35
 
 
36
 
using namespace std;
37
 
using namespace unity::api::scopes;
38
 
 
39
 
// Simple queue that stores query-string/reply pairs, using MyQuery* as a key for removal.
40
 
// The put() method adds a pair at the tail, and the get() method returns a pair at the head.
41
 
// get() suspends the caller until an item is available or until the queue is told to finish.
42
 
// get() returns true if it returns a pair, false if the queue was told to finish.
43
 
// remove() searches for the entry with the given key and erases it.
44
 
 
45
 
class MyQuery;
46
 
 
47
 
class Queue
48
 
{
49
 
public:
50
 
    void put(MyQuery const* query, string const& query_string, ReplyProxy const& reply_proxy)
51
 
    {
52
 
        std::lock_guard<std::mutex> lock(mutex_);
53
 
        queries_.push_back(QueryData { query, query_string, reply_proxy });
54
 
        condvar_.notify_one();
55
 
    }
56
 
 
57
 
    bool get(string& query_string, ReplyProxy& reply_proxy)
58
 
    {
59
 
        std::unique_lock<std::mutex> lock(mutex_);
60
 
        condvar_.wait(lock, [this] { return !queries_.empty() || done_; });
61
 
        if (done_)
62
 
        {
63
 
            condvar_.notify_all();
64
 
        }
65
 
        else
66
 
        {
67
 
            auto qd = queries_.front();
68
 
            queries_.pop_front();
69
 
            query_string = qd.query_string;
70
 
            reply_proxy = qd.reply_proxy;
71
 
        }
72
 
        return !done_;
73
 
    }
74
 
 
75
 
    void remove(MyQuery const* query)
76
 
    {
77
 
        std::lock_guard<std::mutex> lock(mutex_);
78
 
        QueryData qd { query, "", nullptr };
79
 
        auto it = std::find(queries_.begin(), queries_.end(), qd);
80
 
        if (it != queries_.end())
81
 
        {
82
 
            cerr << "Queue: removed query: " << it->query_string << endl;
83
 
            queries_.erase(it);
84
 
        }
85
 
        else
86
 
        {
87
 
            cerr << "Queue: did not find entry to be removed" << endl;
88
 
        }
89
 
    }
90
 
 
91
 
    void finish()
92
 
    {
93
 
        std::unique_lock<std::mutex> lock(mutex_);
94
 
        queries_.clear();
95
 
        done_ = true;
96
 
        condvar_.notify_all();
97
 
    }
98
 
 
99
 
    Queue()
100
 
        : done_(false)
101
 
    {
102
 
    }
103
 
 
104
 
private:
105
 
    struct QueryData
106
 
    {
107
 
        MyQuery const* query;
108
 
        string query_string;
109
 
        ReplyProxy reply_proxy;
110
 
 
111
 
        bool operator==(QueryData const& rhs) const
112
 
        {
113
 
            return query == rhs.query;
114
 
        }
115
 
    };
116
 
 
117
 
    std::list<QueryData> queries_;
118
 
    bool done_;
119
 
    std::mutex mutex_;
120
 
    std::condition_variable condvar_;
121
 
};
122
 
 
123
 
class MyQuery : public QueryBase
124
 
{
125
 
public:
126
 
    MyQuery(string const& scope_name,
127
 
            string const& query,
128
 
            Queue& queue) :
129
 
        scope_name_(scope_name),
130
 
        query_(query),
131
 
        queue_(queue)
132
 
    {
133
 
        cerr << "query instance for \"" << scope_name_ << ":" << query << "\" created" << endl;
134
 
    }
135
 
 
136
 
    virtual ~MyQuery() noexcept
137
 
    {
138
 
        cerr << "query instance for \"" << scope_name_ << ":" << query_ << "\" destroyed" << endl;
139
 
    }
140
 
 
141
 
    virtual void cancelled() override
142
 
    {
143
 
        // Informational callback to let a query know when it was cancelled. The query should
144
 
        // clean up any resources it has allocated, stop pushing results, and arrange for
145
 
        // run() to return (if still active).
146
 
        // Ignoring query cancelltion does not do any direct harm, but wastes CPU cycles: any
147
 
        // results that are pushed once a query is cancelled are ignored anyway.
148
 
        // The main purpose of this callback to give queries a chance to stop doing whatever
149
 
        // work may still be in progress on a query. Note that cancellations are frequent;
150
 
        // not responding to cancelled() correctly causes loss of performance.
151
 
 
152
 
        cerr << "query for \"" << scope_name_ << ":" << query_ << "\" cancelled" << endl;
153
 
    }
154
 
 
155
 
    virtual void run(ReplyProxy const& reply) override
156
 
    {
157
 
        // The query can do anything it likes with this method, that is, run() can push results
158
 
        // directly on the provided reply, or it can save the reply for later use and return from
159
 
        // run(). It is OK to push results on the reply from a different thread.
160
 
        // The only obligation on run() is that, if cancelled() is called, and run() is still active
161
 
        // at that time, run() must tidy up and return in a timely fashion.
162
 
        queue_.put(this, query_, reply);
163
 
    }
164
 
 
165
 
private:
166
 
    string scope_name_;
167
 
    string query_;
168
 
    Queue& queue_;
169
 
};
170
 
 
171
 
// Example scope D: replies asynchronously to queries.
172
 
// The scope's run() method is used as a worker thread that pulls queries from a queue.
173
 
// The MyQuery object's run() method adds the query string and the reply proxy to the queue
174
 
// and signals the worker thread, and then returns. The worker thread pushes the results.
175
 
 
176
 
class MyScope : public ScopeBase
177
 
{
178
 
public:
179
 
    virtual int start(string const& scope_name, RegistryProxy const&) override
180
 
    {
181
 
        scope_name_ = scope_name;
182
 
        return VERSION;
183
 
    }
184
 
 
185
 
    virtual void stop() override
186
 
    {
187
 
        queue_.finish();
188
 
        done_.store(true);
189
 
    }
190
 
 
191
 
    virtual void run() override
192
 
    {
193
 
        // What run() does is up to the scope. For example, we could set up and run an event loop here.
194
 
        // It's OK for run() to be empty and return immediately, or to take as long it likes to complete.
195
 
        // The only obligation is that, if the scopes run time calls stop(), run() must tidy up and return
196
 
        // in as timely a manner as possible.
197
 
        while (!done_.load())
198
 
        {
199
 
            string query;
200
 
            ReplyProxy reply_proxy;
201
 
            if (queue_.get(query, reply_proxy) && !done_.load())
202
 
            {
203
 
                for (int i = 1; i < 5; ++i)
204
 
                {
205
 
                    auto cat = std::make_shared<Category>("cat1");
206
 
                    ResultItem result(cat);
207
 
                    result.set_uri("uri");
208
 
                    result.set_title(scope_name_ + ": result " + to_string(i) + " for query \"" + query + "\"");
209
 
                    result.set_icon("icon");
210
 
                    result.set_dnd_uri("dnd_uri");
211
 
                    if (!reply_proxy->push(result))
212
 
                    {
213
 
                        break; // Query was cancelled
214
 
                    }
215
 
                    sleep(1);
216
 
                }
217
 
                cerr << scope_name_ << ": query \"" << query << "\" complete" << endl;
218
 
            }
219
 
        }
220
 
    }
221
 
 
222
 
    virtual QueryBase::UPtr create_query(string const& q, VariantMap const&) override
223
 
    {
224
 
        QueryBase::UPtr query(new MyQuery(scope_name_, q, queue_));
225
 
        cerr << scope_name_ << ": created query: \"" << q << "\"" << endl;
226
 
        return query;
227
 
    }
228
 
 
229
 
    MyScope()
230
 
        : done_(false)
231
 
    {
232
 
    }
233
 
 
234
 
private:
235
 
    string scope_name_;
236
 
    Queue queue_;
237
 
    std::atomic_bool done_;
238
 
};
239
 
 
240
 
// External entry points to allocate and deallocate the scope.
241
 
 
242
 
extern "C"
243
 
{
244
 
 
245
 
    EXPORT
246
 
    unity::api::scopes::ScopeBase*
247
 
    // cppcheck-suppress unusedFunction
248
 
    UNITY_API_SCOPE_CREATE_FUNCTION()
249
 
    {
250
 
        return new MyScope;
251
 
    }
252
 
 
253
 
    EXPORT
254
 
    void
255
 
    // cppcheck-suppress unusedFunction
256
 
    UNITY_API_SCOPE_DESTROY_FUNCTION(unity::api::scopes::ScopeBase* scope_base)
257
 
    {
258
 
        delete scope_base;
259
 
    }
260
 
 
261
 
}