2
* Copyright (C) 2013 Canonical Ltd
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.
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.
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/>.
16
* Authored by: Michi Henning <michi.henning@canonical.com>
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>
27
#include <condition_variable>
34
#define EXPORT __attribute__ ((visibility ("default")))
37
using namespace unity::api::scopes;
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.
50
void put(MyQuery const* query, string const& query_string, ReplyProxy const& reply_proxy)
52
std::lock_guard<std::mutex> lock(mutex_);
53
queries_.push_back(QueryData { query, query_string, reply_proxy });
54
condvar_.notify_one();
57
bool get(string& query_string, ReplyProxy& reply_proxy)
59
std::unique_lock<std::mutex> lock(mutex_);
60
condvar_.wait(lock, [this] { return !queries_.empty() || done_; });
63
condvar_.notify_all();
67
auto qd = queries_.front();
69
query_string = qd.query_string;
70
reply_proxy = qd.reply_proxy;
75
void remove(MyQuery const* query)
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())
82
cerr << "Queue: removed query: " << it->query_string << endl;
87
cerr << "Queue: did not find entry to be removed" << endl;
93
std::unique_lock<std::mutex> lock(mutex_);
96
condvar_.notify_all();
107
MyQuery const* query;
109
ReplyProxy reply_proxy;
111
bool operator==(QueryData const& rhs) const
113
return query == rhs.query;
117
std::list<QueryData> queries_;
120
std::condition_variable condvar_;
123
class MyQuery : public QueryBase
126
MyQuery(string const& scope_name,
129
scope_name_(scope_name),
133
cerr << "query instance for \"" << scope_name_ << ":" << query << "\" created" << endl;
136
virtual ~MyQuery() noexcept
138
cerr << "query instance for \"" << scope_name_ << ":" << query_ << "\" destroyed" << endl;
141
virtual void cancelled() override
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.
152
cerr << "query for \"" << scope_name_ << ":" << query_ << "\" cancelled" << endl;
155
virtual void run(ReplyProxy const& reply) override
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);
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.
176
class MyScope : public ScopeBase
179
virtual int start(string const& scope_name, RegistryProxy const&) override
181
scope_name_ = scope_name;
185
virtual void stop() override
191
virtual void run() override
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())
200
ReplyProxy reply_proxy;
201
if (queue_.get(query, reply_proxy) && !done_.load())
203
for (int i = 1; i < 5; ++i)
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))
213
break; // Query was cancelled
217
cerr << scope_name_ << ": query \"" << query << "\" complete" << endl;
222
virtual QueryBase::UPtr create_query(string const& q, VariantMap const&) override
224
QueryBase::UPtr query(new MyQuery(scope_name_, q, queue_));
225
cerr << scope_name_ << ": created query: \"" << q << "\"" << endl;
237
std::atomic_bool done_;
240
// External entry points to allocate and deallocate the scope.
246
unity::api::scopes::ScopeBase*
247
// cppcheck-suppress unusedFunction
248
UNITY_API_SCOPE_CREATE_FUNCTION()
255
// cppcheck-suppress unusedFunction
256
UNITY_API_SCOPE_DESTROY_FUNCTION(unity::api::scopes::ScopeBase* scope_base)