-
Notifications
You must be signed in to change notification settings - Fork 1
/
Task.h
151 lines (138 loc) · 8.62 KB
/
Task.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#ifndef PCSC_CENXFS_BRIDGE_Task_H
#define PCSC_CENXFS_BRIDGE_Task_H
#pragma once
#include <boost/chrono/chrono.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread/recursive_mutex.hpp>
#include <boost/thread/lock_guard.hpp>
// Контейнер для хранения задач на чтение карточки.
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/composite_key.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/mem_fun.hpp>
// PC/CS API -- для SCARD_READERSTATE
#include <winscard.h>
// Определения для ридеров карт (Identification card unit (IDC)) --
// для REQUESTID, HSERVICE и WFS_ERR_*
#include <XFSIDC.h>
namespace mi = boost::multi_index;
namespace bc = boost::chrono;
class Service;
/** Класс для хранения информации, необходимой для посылки EXECUTE событий после
наступления оных окну, которое были указаны в качестве приемника при вызове
функции `WFPExecute`.
*/
class Task {
public:
/// Время, когда истекает таймаут для данной задачи.
bc::steady_clock::time_point deadline;
/// Сервис, который создал эту задачу.
Service& mService;
/// Окно, которое получит уведомление о завершении задачи.
HWND hWnd;
/// Трекинговый номер данной задачи, который будет предоставлен в уведомлении окну `hWnd`.
/// Должен быть уникален для каждой задачи.
REQUESTID ReqID;
public:
typedef boost::shared_ptr<Task> Ptr;
public:
Task(bc::steady_clock::time_point deadline, Service& service, HWND hWnd, REQUESTID ReqID)
: deadline(deadline), mService(service), hWnd(hWnd), ReqID(ReqID) {}
inline bool operator<(const Task& other) const {
return deadline < other.deadline;
}
inline bool operator==(const Task& other) const {
return &mService == &other.mService && ReqID == other.ReqID;
}
public:
/** Проверяет, является ли указанное событие тем, что ожидает данная задача.
Если функция вернет `true`, та данная задача будет считаться завершенной
и будет исключена из списка зарегистрированных задач. Если задаче требуется
выполнить какие-то действия в случае успешного завершения, это надо сделать
здесь.
@param state
Данные изменившегося состояния.
@param deviceChange
Если `true`, то изменение относится к изменению количества устройств, а не
карточки в устройстве.
@return
`true`, если задача дождалась интересующего ее события и должна быть удалена
из очереди задач, иначе `false`.
*/
virtual bool match(const SCARD_READERSTATE& state, bool deviceChange) const = 0;
/// Уведомляет XFS-слушателя о завершении ожидания.
/// @param result Код ответа для завершения.
void complete(HRESULT result) const;
/// Вызывается, если запрос был отменен вызовом WFPCancelAsyncRequest.
inline void cancel() const { complete(WFS_ERR_CANCELED); }
inline void timeout() const { complete(WFS_ERR_TIMEOUT); }
HSERVICE serviceHandle() const;
};
/// Содержит список задач и методы для их потокобезопасного добавления, отмены и обработки.
class TaskContainer {
typedef mi::multi_index_container<
Task::Ptr,
mi::indexed_by<
// Сортировка по CardWaitTask::operator< -- по времени дедлайна, для выбора
// задач, чей таймаут подошел. В один момент времени может завершиться несколько
// задач, поэтому индекс неуникальный.
mi::ordered_non_unique<mi::identity<Task> >,
// Сортировка по less<REQUESTID> по ReqID -- для удаления отмененных задач,
// но ReqID уникален в пределах сервиса.
mi::ordered_unique<mi::composite_key<
Task,
BOOST_MULTI_INDEX_CONST_MEM_FUN(Task, HSERVICE, serviceHandle),
mi::member<Task, REQUESTID, &Task::ReqID>
> >
>
> TaskList;
private:
/// Список задач, упорядоченый по возрастанию времени дедлайна.
/// Чем раньше дедлайн, тем ближе задача к голове списка.
TaskList tasks;
/// Мьютекс для защиты `tasks` от одновременной модификации.
mutable boost::recursive_mutex tasksMutex;
public:
/// При разрушении контейнера все задачи отменяются.
~TaskContainer();
public:
/** Добавляет задачу в очередь, возвращает `true`, если задача первая в очереди
и дедлайн необходимо скорректировать, чтобы не пропустить дедлайн данной задачи.
@param task
Новая задача.
@return
Если дедлайн новой задачи окажется спереди всех прочих задач, то метод возвращает
`true`. Это означает, что нужно обновить таймаут ожидания событий от считывателей,
чтобы не пропустить таймаут данной задачи. Если время ближайшего дедлайна не меняется,
возвращает `false`.
*/
bool addTask(const Task::Ptr& task);
/** Отменяет задачу, созданную указанным сервис-провайдером и имеющую указанный
трекинговый номер.
@param hService
Сервис, поставивший задачу в очередб на выполнение и теперь желающий отменить ее.
@param ReqID
Номер задачи, присвоенный ей XFS менеджером.
@return
`true`, если задача была найдена (в этом случае она будет отменена), иначе `false`,
что означает, что задачи с такими параметрами не существует в очереди задач.
*/
bool cancelTask(HSERVICE hService, REQUESTID ReqID);
/// Вычисляет таймаут до ближайшего дедлайна потокобезопасным способом.
DWORD getTimeout() const;
/** Удаляет из списка все задачи, чье время дедлайна раньше или равно указанному
и сигнализирует зарегистрированным в задаче слушателем о наступлении таймаута.
@param now
Время, для которого рассматривается, наступил таймаут или нет.
*/
void processTimeouts(bc::steady_clock::time_point now);
/** Уведомляет все задачи об изменении в считывателе. В результате некоторые задачи могут завершиться.
@param state
Состояние изменившегося считывателя, в том числе это может быть изменение
количества считывателей.
*/
void notifyChanges(const SCARD_READERSTATE& state, bool deviceChange);
};
#endif PCSC_CENXFS_BRIDGE_Task_H