Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qabstracteventdispatcher.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
7
8#include "qthread.h"
9#include <private/qthread_p.h>
10#include <private/qcoreapplication_p.h>
11#include <private/qfreelist_p.h>
12
13#include <QtCore/q26numeric.h>
14
16
17using namespace std::chrono_literals;
18
19// we allow for 2^24 = 8^8 = 16777216 simultaneously running timers
21{
22 enum
23 {
26 };
27
28 static const int Sizes[BlockCount];
29};
30
31enum {
32 Offset0 = 0x00000000,
33 Offset1 = 0x00000040,
34 Offset2 = 0x00000100,
35 Offset3 = 0x00001000,
36 Offset4 = 0x00010000,
37 Offset5 = 0x00100000,
38
44 Size5 = QtTimerIdFreeListConstants::MaxIndex - Offset5
45};
46
55
57Q_GLOBAL_STATIC(QtTimerIdFreeList, timerIdFreeList)
58
59template <typename T> static T fromDuration(std::chrono::nanoseconds interval)
60{
61 using namespace std::chrono;
62 qint64 value = ceil<milliseconds>(interval).count();
63 return q26::saturate_cast<T>(value);
64}
65
66#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
67static inline QAbstractEventDispatcherV2 *v2(QAbstractEventDispatcher *self)
68{
69 if (QAbstractEventDispatcherPrivate::get(self)->isV2)
70 return static_cast<QAbstractEventDispatcherV2 *>(self);
71 return nullptr;
72}
73
74static inline const QAbstractEventDispatcherV2 *v2(const QAbstractEventDispatcher *self)
75{
76 if (QAbstractEventDispatcherPrivate::get(self)->isV2)
77 return static_cast<const QAbstractEventDispatcherV2 *>(self);
78 return nullptr;
79}
80#endif // Qt 7
81
82QAbstractEventDispatcherPrivate::QAbstractEventDispatcherPrivate()
83{
84 // Create the timer ID free list here to make sure that it is destroyed
85 // after any global static thread that may be using it.
86 // See also QTBUG-58732.
87 if (!timerIdFreeList.isDestroyed())
88 (void)timerIdFreeList();
89}
90
91QAbstractEventDispatcherPrivate::~QAbstractEventDispatcherPrivate()
92 = default;
93
94int QAbstractEventDispatcherPrivate::allocateTimerId()
95{
96 // This function may be called after timerIdFreeList() has been destructed
97 // for example in case when application exits without waiting for
98 // running threads to exit and running thread finished() has been connected
99 // to a slot which triggers a sequence that registers new timer.
100 // See https://bugreports.qt-project.org/browse/QTBUG-38957.
101 if (QtTimerIdFreeList *fl = timerIdFreeList())
102 return fl->next();
103 return 0; // Note! returning 0 generates a warning
104}
105
106void QAbstractEventDispatcherPrivate::releaseTimerId(int timerId)
107{
108 // this function may be called by a global destructor after
109 // timerIdFreeList() has been destructed
110 if (QtTimerIdFreeList *fl = timerIdFreeList())
111 fl->release(timerId);
112}
113
114/*!
115 \class QAbstractEventDispatcher
116 \inmodule QtCore
117 \brief The QAbstractEventDispatcher class provides an interface to manage Qt's event queue.
118
119 \ingroup events
120
121 An event dispatcher receives events from the window system and other
122 sources. It then sends them to the QCoreApplication or QApplication
123 instance for processing and delivery. QAbstractEventDispatcher provides
124 fine-grained control over event delivery.
125
126 For simple control of event processing use
127 QCoreApplication::processEvents().
128
129 For finer control of the application's event loop, call
130 instance() and call functions on the QAbstractEventDispatcher
131 object that is returned. If you want to use your own instance of
132 QAbstractEventDispatcher or of a QAbstractEventDispatcher
133 subclass, you must install it with QCoreApplication::setEventDispatcher()
134 or QThread::setEventDispatcher() \e before a default event dispatcher has
135 been installed.
136
137 The main event loop is started by calling
138 QCoreApplication::exec(), and stopped by calling
139 QCoreApplication::exit(). Local event loops can be created using
140 QEventLoop.
141
142 Programs that perform long operations can call processEvents()
143 with a bitwise OR combination of various QEventLoop::ProcessEventsFlag
144 values to control which events should be delivered.
145
146 QAbstractEventDispatcher also allows the integration of an
147 external event loop with the Qt event loop.
148
149 \sa QEventLoop, QCoreApplication, QThread
150*/
151/*!
152 \typedef QAbstractEventDispatcher::Duration
153
154 A \c{std::chrono::duration} type that is used in various API in this class.
155 This type exists to facilitate a possible transition to a higher or lower
156 granularity.
157
158 In all current platforms, it is \c nanoseconds.
159*/
160
161/*!
162 Constructs a new event dispatcher with the given \a parent.
163*/
164QAbstractEventDispatcher::QAbstractEventDispatcher(QObject *parent)
165 : QObject(*new QAbstractEventDispatcherPrivate, parent) {}
166
167/*!
168 \internal
169*/
170QAbstractEventDispatcher::QAbstractEventDispatcher(QAbstractEventDispatcherPrivate &dd,
171 QObject *parent)
172 : QObject(dd, parent) {}
173
174/*!
175 Destroys the event dispatcher.
176*/
177QAbstractEventDispatcher::~QAbstractEventDispatcher()
178{
179 // don't recreate the QThreadData if it has already been destroyed
180 QThreadData *data = QThreadData::currentThreadData();
181 if (data && data->eventDispatcher.loadRelaxed() == this)
182 data->eventDispatcher.storeRelaxed(nullptr);
183}
184
185/*!
186 Returns a pointer to the event dispatcher object for the specified
187 \a thread. If \a thread is \nullptr, the current thread is used. If no
188 event dispatcher exists for the specified thread, this function
189 returns \nullptr.
190
191 \b{Note:} If Qt is built without thread support, the \a thread
192 argument is ignored.
193 */
194QAbstractEventDispatcher *QAbstractEventDispatcher::instance(QThread *thread)
195{
196 // do create a QThreadData, in case this is very early in an adopted thread
197 QThreadData *data = thread ? QThreadData::get2(thread) : QThreadData::current();
198 return data->eventDispatcher.loadRelaxed();
199}
200
201/*!
202 \fn bool QAbstractEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
203
204 Processes pending events that match \a flags until there are no
205 more events to process. Returns \c true if an event was processed;
206 otherwise returns \c false.
207
208 This function is especially useful if you have a long running
209 operation, and want to show its progress without allowing user
210 input by using the QEventLoop::ExcludeUserInputEvents flag.
211
212 If the QEventLoop::WaitForMoreEvents flag is set in \a flags, the
213 behavior of this function is as follows:
214
215 \list
216
217 \li If events are available, this function returns after processing
218 them.
219
220 \li If no events are available, this function will wait until more
221 are available and return after processing newly available events.
222
223 \endlist
224
225 If the QEventLoop::WaitForMoreEvents flag is not set in \a flags,
226 and no events are available, this function will return
227 immediately.
228
229 \b{Note:} This function does not process events continuously; it
230 returns after all available events are processed.
231*/
232
233/*!
234 \internal
235
236 \note processEvents() only processes events queued before the function
237 is called. Events that are posted while the function runs will be queued
238 until a later round of event processing. This only applies to posted Qt
239 events. For timers and system level events, the situation is unknown.
240*/
241
242/*!
243 \fn void QAbstractEventDispatcher::registerSocketNotifier(QSocketNotifier *notifier)
244
245 Registers \a notifier with the event loop. Subclasses must
246 implement this method to tie a socket notifier into another
247 event loop.
248*/
249
250/*! \fn void QAbstractEventDispatcher::unregisterSocketNotifier(QSocketNotifier *notifier)
251
252 Unregisters \a notifier from the event dispatcher. Subclasses must
253 reimplement this method to tie a socket notifier into another
254 event loop. Reimplementations must call the base
255 implementation.
256*/
257
258/*!
259 \obsolete [6.8] This function will be removed in Qt 7. Use the overload taking \l Duration.
260
261 Registers a timer with the specified \a interval and \a timerType for the
262 given \a object and returns the timer id.
263*/
264int QAbstractEventDispatcher::registerTimer(qint64 interval, Qt::TimerType timerType, QObject *object)
265{
266 return int(registerTimer(interval * 1ms, timerType, object));
267}
268
269/*!
270 \since 6.8
271 \overload
272
273 Registers a timer with the specified \a interval and \a timerType for the
274 given \a object and returns the timer id.
275*/
276Qt::TimerId QAbstractEventDispatcher::registerTimer(Duration interval, Qt::TimerType timerType,
277 QObject *object)
278{
279 auto id = Qt::TimerId(QAbstractEventDispatcherPrivate::allocateTimerId());
280#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
281 if (QAbstractEventDispatcherV2 *self = v2(this))
282 self->registerTimer(id, interval, timerType, object);
283 else
284 registerTimer(qToUnderlying(id), fromDuration<qint64>(interval), timerType, object);
285#else
286 registerTimer(id, interval, timerType, object);
287#endif
288 return id;
289}
290
291#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
292/*!
293 \fn void QAbstractEventDispatcher::registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object)
294
295 Register a timer with the specified \a timerId, \a interval, and \a
296 timerType for the given \a object.
297*/
298
299/*!
300 \fn bool QAbstractEventDispatcher::unregisterTimer(int timerId)
301
302 Unregisters the timer with the given \a timerId.
303 Returns \c true if successful; otherwise returns \c false.
304
305 \sa registerTimer(), unregisterTimers()
306*/
307
308/*!
309 \fn QList<TimerInfo> QAbstractEventDispatcher::registeredTimers(QObject *object) const
310
311 Returns a list of registered timers for \a object. The TimerInfo struct has
312 \c timerId, \c interval, and \c timerType members.
313
314 \sa Qt::TimerType
315*/
316
317/*!
318 \fn int QAbstractEventDispatcher::remainingTime(int timerId)
319
320 Returns the remaining time in milliseconds with the given \a timerId.
321 If the timer is inactive, the returned value will be -1. If the timer is
322 overdue, the returned value will be 0.
323
324 \sa Qt::TimerType
325*/
326#else // Qt 7
327/*!
328 \fn void QAbstractEventDispatcher::registerTimer(Qt::TimerId timerId, Duration interval, Qt::TimerType timerType, QObject *object)
329 \since 6.8
330
331 Register a timer with the specified \a timerId, \a interval, and \a
332 timerType for the given \a object.
333
334 \sa unregisterTimer(), timersForObject()
335*/
336
337/*!
338 \fn bool QAbstractEventDispatcher::unregisterTimer(Qt::TimerId timerId)
339 \since 6.8
340
341 Unregisters the timer with the given \a timerId.
342 Returns \c true if successful; otherwise returns \c false.
343
344 \sa registerTimer(), unregisterTimers()
345*/
346
347/*!
348 \fn QList<TimerInfoV2> QAbstractEventDispatcher::timersForObject(QObject *object) const
349 \since 6.8
350
351 Returns a list of registered timers for \a object. The TimerInfoV2 struct has
352 \c timerId, \c interval, and \c timerType members.
353
354 \sa Qt::TimerType, registerTimer(), unregisterTimer()
355*/
356
357/*!
358 \fn QAbstractEventDispatcher::remainingTime(Qt::TimerId timerId) const
359
360 Returns the remaining time of the timer with the given \a timerId.
361 If the timer is inactive, the returned value will be negative. If the timer
362 is overdue, the returned value will be 0.
363
364 \sa Qt::TimerType, registerTimer(), unregisterTimer()
365*/
366#endif
367
368/*!
369 \fn bool QAbstractEventDispatcher::unregisterTimers(QObject *object)
370
371 Unregisters all the timers associated with the given \a object. Returns \c
372 true if all timers were successfully removed; otherwise returns \c false.
373
374 \sa unregisterTimer(), registeredTimers()
375*/
376
377
378/*! \fn void QAbstractEventDispatcher::wakeUp()
379 \threadsafe
380
381 Wakes up the event loop.
382
383 \omit
384 ### FIXME - QTBUG-70229
385 On Unix and Glib event dispatchers, if the dispatcher is already awake when
386 this function is called, it is ensured that the current iteration won't block
387 waiting for more events, but will instead do another event loop iteration.
388
389 ### TODO - does other event dispatchers behave the same?
390 \endomit
391
392 \sa awake()
393*/
394
395/*!
396 \fn void QAbstractEventDispatcher::interrupt()
397
398 Interrupts event dispatching. The event dispatcher will
399 return from processEvents() as soon as possible.
400*/
401
402// ### DOC: Are these called when the _application_ starts/stops or just
403// when the current _event loop_ starts/stops?
404/*!
405 \internal
406*/
407void QAbstractEventDispatcher::startingUp()
408{ }
409
410/*!
411 \internal
412*/
413void QAbstractEventDispatcher::closingDown()
414{ }
415
416/*!
417 \class QAbstractEventDispatcher::TimerInfo
418 \deprecated [6.8] Use TimerInfoV2
419 \inmodule QtCore
420
421 This struct represents information about a timer:
422 \l{QAbstractEventDispatcher::TimerInfo::timerId}{timerId},
423 \l{QAbstractEventDispatcher::TimerInfo::interval}{interval}, and
424 \l{QAbstractEventDispatcher::TimerInfo::timerType}{timerType}.
425
426 \sa registeredTimers(), QAbstractEventDispatcher::TimerInfoV2, timersForObject()
427*/
428/*! \fn QAbstractEventDispatcher::TimerInfo::TimerInfo(int timerId, int interval, Qt::TimerType timerType)
429
430 Constructs a TimerInfo struct with the given \a timerId, \a interval, and
431 \a timerType.
432*/
433/*!
434 \variable QAbstractEventDispatcher::TimerInfo::timerId
435
436 The timer's unique id.
437*/
438/*!
439 \variable QAbstractEventDispatcher::TimerInfo::interval
440
441 The timer's interval.
442*/
443/*!
444 \variable QAbstractEventDispatcher::TimerInfo::timerType
445
446 The timer's type
447
448 \sa Qt::TimerType
449*/
450
451/*!
452 \class QAbstractEventDispatcher::TimerInfoV2
453 \inmodule QtCore
454
455 This struct represents information about a timer:
456 \l{QAbstractEventDispatcher::TimerInfoV2::timerId}{timerId},
457 \l{QAbstractEventDispatcher::TimerInfoV2::interval}{interval}, and
458 \l{QAbstractEventDispatcher::TimerInfoV2::timerType}{timerType}.
459
460 \sa timersForObject()
461*/
462/*!
463 \variable QAbstractEventDispatcher::TimerInfoV2::timerId
464
465 The timer's unique id. This is created by registerTimer() upon creation and
466 uniquely identifies a timer while it is active. It is also used by
467 QTimer::id() and returned by QObject::startTimer().
468*/
469/*!
470 \variable QAbstractEventDispatcher::TimerInfoV2::interval
471
472 The timer's interval.
473*/
474/*!
475 \variable QAbstractEventDispatcher::TimerInfoV2::timerType
476
477 The timer's type
478
479 \sa Qt::TimerType
480*/
481
482/*!
483 Installs an event filter \a filterObj for all native events received by the application.
484
485 The event filter \a filterObj receives events via its \l {QAbstractNativeEventFilter::}{nativeEventFilter()}
486 function, which is called for all events received by all threads.
487
488 The \l {QAbstractNativeEventFilter::}{nativeEventFilter()} function should return true
489 if the event should be filtered, (in this case, stopped). It should return false to allow
490 normal Qt processing to continue: the native event can then be translated
491 into a QEvent and handled by the standard Qt \l{QEvent} {event} filtering,
492 e.g. QObject::installEventFilter().
493
494 If multiple event filters are installed, the filter that was installed last
495 is activated first.
496
497 \note The filter function set here receives native messages,
498 that is, MSG or XEvent structs.
499
500 For maximum portability, you should always try to use QEvent objects
501 and QObject::installEventFilter() whenever possible.
502
503 \sa QObject::installEventFilter()
504
505 \since 5.0
506*/
507void QAbstractEventDispatcher::installNativeEventFilter(QAbstractNativeEventFilter *filterObj)
508{
509 Q_D(QAbstractEventDispatcher);
510
511 // clean up unused items in the list
512 d->eventFilters.removeAll(nullptr);
513 d->eventFilters.removeAll(filterObj);
514 d->eventFilters.prepend(filterObj);
515}
516
517/*!
518 Removes the event filter \a filter from this object. The
519 request is ignored if such an event filter has not been installed.
520
521 All event filters for this object are automatically removed when
522 this object is destroyed.
523
524 It is always safe to remove an event filter, even during event filter
525 filter activation (that is, even from within the \l {QAbstractNativeEventFilter::}{nativeEventFilter()} function).
526
527 \sa installNativeEventFilter(), QAbstractNativeEventFilter
528 \since 5.0
529*/
530void QAbstractEventDispatcher::removeNativeEventFilter(QAbstractNativeEventFilter *filter)
531{
532 Q_D(QAbstractEventDispatcher);
533 for (int i = 0; i < d->eventFilters.size(); ++i) {
534 if (d->eventFilters.at(i) == filter) {
535 d->eventFilters[i] = nullptr;
536 break;
537 }
538 }
539}
540
541/*!
542 Sends \a message through the event filters that were set by
543 installNativeEventFilter(). This function returns \c true as soon as an
544 event filter returns \c true, and false otherwise to indicate that
545 the processing of the event should continue.
546
547 Subclasses of QAbstractEventDispatcher \e must call this function
548 for \e all messages received from the system to ensure
549 compatibility with any extensions that may be used in the
550 application. The type of event \a eventType is specific to the platform
551 plugin chosen at run-time, and can be used to cast message to the right type.
552 The \a result pointer is only used on Windows, and corresponds to the LRESULT pointer.
553
554 Note that the type of \a message is platform dependent. See
555 QAbstractNativeEventFilter for details.
556
557 \sa installNativeEventFilter(), QAbstractNativeEventFilter::nativeEventFilter()
558 \since 5.0
559*/
560bool QAbstractEventDispatcher::filterNativeEvent(const QByteArray &eventType, void *message, qintptr *result)
561{
562 Q_D(QAbstractEventDispatcher);
563 if (!d->eventFilters.isEmpty()) {
564 // Raise the loopLevel so that deleteLater() calls in or triggered
565 // by event_filter() will be processed from the main event loop.
566 QScopedScopeLevelCounter scopeLevelCounter(d->threadData.loadAcquire());
567 for (int i = 0; i < d->eventFilters.size(); ++i) {
568 QAbstractNativeEventFilter *filter = d->eventFilters.at(i);
569 if (!filter)
570 continue;
571 if (filter->nativeEventFilter(eventType, message, result))
572 return true;
573 }
574 }
575 return false;
576}
577
578/*! \fn void QAbstractEventDispatcher::awake()
579
580 This signal is emitted after the event loop returns from a
581 function that could block.
582
583 \sa wakeUp(), aboutToBlock()
584*/
585
586/*! \fn void QAbstractEventDispatcher::aboutToBlock()
587
588 This signal is emitted before the event loop calls a function that
589 could block.
590
591 \sa awake()
592*/
593
594#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
595void QAbstractEventDispatcher::registerTimer(Qt::TimerId timerId, Duration interval,
596 Qt::TimerType timerType, QObject *object)
597{
598 if (QAbstractEventDispatcherV2 *self = v2(this))
599 self->registerTimer(timerId, interval, timerType, object);
600 else
601 registerTimer(int(timerId), fromDuration<qint64>(interval), timerType, object);
602}
603
604bool QAbstractEventDispatcher::unregisterTimer(Qt::TimerId timerId)
605{
606 if (QAbstractEventDispatcherV2 *self = v2(this))
607 return self->unregisterTimer(timerId);
608 return unregisterTimer(int(timerId));
609}
610
611QList<QAbstractEventDispatcher::TimerInfoV2>
612QAbstractEventDispatcher::timersForObject(QObject *object) const
613{
614 if (const QAbstractEventDispatcherV2 *self = v2(this))
615 return self->timersForObject(object);
616 QList<TimerInfo> timers = registeredTimers(object);
617 QList<TimerInfoV2> result;
618 result.reserve(timers.size());
619 for (const TimerInfo &t : timers)
620 result.emplaceBack(TimerInfoV2{ t.interval * 1ms, Qt::TimerId(t.timerId), t.timerType });
621 return result;
622}
623
624QAbstractEventDispatcher::Duration
625QAbstractEventDispatcher::remainingTime(Qt::TimerId timerId) const
626{
627 if (const QAbstractEventDispatcherV2 *self = v2(this))
628 return self->remainingTime(timerId);
629 return const_cast<QAbstractEventDispatcher *>(this)->remainingTime(int(timerId)) * 1ms;
630}
631
632/*!
633 \class QAbstractEventDispatcherV2
634 \inmodule QtCore
635
636 This class is a temporary hack to enable transition to an API based on
637 \c{std::chrono} for the Qt event dispatcher. In Qt 7, it will be merged
638 with QAbstractEventDispatcher, replacing the pure virtuals there with the
639 ones defined here.
640
641 It is recommended applications and libraries port to the new API before
642 that future release to simplify work when the time comes.
643*/
644
645/*!
646 Constructs a new event dispatcher with the given \a parent.
647*/
648QAbstractEventDispatcherV2::QAbstractEventDispatcherV2(QObject *parent)
649 : QAbstractEventDispatcherV2(*new QAbstractEventDispatcherPrivate, parent)
650{
651}
652
653/*!
654 \internal
655*/
656QAbstractEventDispatcherV2::QAbstractEventDispatcherV2(QAbstractEventDispatcherPrivate &dd,
657 QObject *parent)
658 : QAbstractEventDispatcher((dd.isV2 = true, dd), parent)
659{
660}
661
662/*!
663 Destroys the event dispatcher.
664*/
665QAbstractEventDispatcherV2::~QAbstractEventDispatcherV2() = default;
666
667/*!
668 \internal
669 Temporary compatibility override.
670*/
671void QAbstractEventDispatcherV2::registerTimer(int timerId, qint64 interval,
672 Qt::TimerType timerType, QObject *object)
673{
674 auto self = static_cast<QAbstractEventDispatcherV2 *>(this);
675 self->registerTimer(Qt::TimerId(timerId), interval * 1ms, timerType, object);
676}
677
678/*!
679 \internal
680 Temporary compatibility override.
681*/
682bool QAbstractEventDispatcherV2::unregisterTimer(int timerId)
683{
684 auto self = static_cast<QAbstractEventDispatcherV2 *>(this);
685 return self->unregisterTimer(Qt::TimerId(timerId));
686}
687
688/*!
689 \internal
690 Temporary compatibility override.
691*/
692auto QAbstractEventDispatcherV2::registeredTimers(QObject *object) const -> QList<TimerInfo>
693{
694 auto self = static_cast<const QAbstractEventDispatcherV2 *>(this);
695 QList<TimerInfoV2> timers = self->timersForObject(object);
696 QList<TimerInfo> result;
697 result.reserve(timers.size());
698 for (const TimerInfoV2 &t : timers)
699 result.emplaceBack(qToUnderlying(t.timerId), fromDuration<int>(t.interval), t.timerType);
700 return result;
701}
702
703/*!
704 \internal
705 Temporary compatibility override.
706*/
707int QAbstractEventDispatcherV2::remainingTime(int timerId)
708{
709 auto self = static_cast<QAbstractEventDispatcherV2 *>(this);
710 return fromDuration<int>(self->remainingTime(Qt::TimerId(timerId)));
711}
712
713/*!
714 \internal
715 Temporary compatibility override.
716*/
717bool QAbstractEventDispatcherV2::processEventsWithDeadline(QEventLoop::ProcessEventsFlags flags, QDeadlineTimer deadline)
718{
719 Q_UNUSED(deadline);
720 return processEvents(flags);
721}
722#endif // ! Qt 7
723
724QT_END_NAMESPACE
725
726#include "moc_qabstracteventdispatcher.cpp"
QFreeList< void, QtTimerIdFreeListConstants > QtTimerIdFreeList
static const int Sizes[BlockCount]