summaryrefslogtreecommitdiffstats
path: root/cpukit/score
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2017-01-31 08:08:24 +0100
committerSebastian Huber <sebastian.huber@embedded-brains.de>2017-01-31 09:38:07 +0100
commite366f774a76d3607ad41dc0992e787ce38df980d (patch)
tree4ba5522aaa87021c60bcaf8d162af9b521b9268c /cpukit/score
parentscore: Fix _Thread_Initialize() (diff)
downloadrtems-e366f774a76d3607ad41dc0992e787ce38df980d.tar.bz2
score: Add _Thread_queue_Object_name
Add the special thread queue name _Thread_queue_Object_name to mark thread queues embedded in an object with identifier. Using the special thread state STATES_THREAD_QUEUE_WITH_IDENTIFIER is not reliable for this purpose since the thread wait information and thread state are protected by different SMP locks in separate critical sections. Remove STATES_THREAD_QUEUE_WITH_IDENTIFIER. Add and use _Thread_queue_Object_initialize(). Update #2858.
Diffstat (limited to 'cpukit/score')
-rw-r--r--cpukit/score/include/rtems/score/coremuteximpl.h2
-rw-r--r--cpukit/score/include/rtems/score/coresemimpl.h2
-rw-r--r--cpukit/score/include/rtems/score/mrspimpl.h2
-rw-r--r--cpukit/score/include/rtems/score/statesimpl.h48
-rw-r--r--cpukit/score/include/rtems/score/threadqimpl.h48
-rw-r--r--cpukit/score/src/corebarrier.c2
-rw-r--r--cpukit/score/src/corebarrierwait.c2
-rw-r--r--cpukit/score/src/coremsg.c2
-rw-r--r--cpukit/score/src/coremsgseize.c2
-rw-r--r--cpukit/score/src/coremsgsubmit.c2
-rw-r--r--cpukit/score/src/coremutexseize.c2
-rw-r--r--cpukit/score/src/corerwlock.c2
-rw-r--r--cpukit/score/src/corerwlockobtainread.c2
-rw-r--r--cpukit/score/src/corerwlockobtainwrite.c2
-rw-r--r--cpukit/score/src/coresem.c2
-rw-r--r--cpukit/score/src/threadinitialize.c3
-rw-r--r--cpukit/score/src/threadq.c46
-rw-r--r--cpukit/score/src/threadwaitgetid.c15
18 files changed, 130 insertions, 56 deletions
diff --git a/cpukit/score/include/rtems/score/coremuteximpl.h b/cpukit/score/include/rtems/score/coremuteximpl.h
index dead048880..e524539419 100644
--- a/cpukit/score/include/rtems/score/coremuteximpl.h
+++ b/cpukit/score/include/rtems/score/coremuteximpl.h
@@ -43,7 +43,7 @@ RTEMS_INLINE_ROUTINE void _CORE_mutex_Initialize(
CORE_mutex_Control *the_mutex
)
{
- _Thread_queue_Initialize( &the_mutex->Wait_queue );
+ _Thread_queue_Object_initialize( &the_mutex->Wait_queue );
}
RTEMS_INLINE_ROUTINE void _CORE_mutex_Destroy( CORE_mutex_Control *the_mutex )
diff --git a/cpukit/score/include/rtems/score/coresemimpl.h b/cpukit/score/include/rtems/score/coresemimpl.h
index 6082c3bd72..20ca30b366 100644
--- a/cpukit/score/include/rtems/score/coresemimpl.h
+++ b/cpukit/score/include/rtems/score/coresemimpl.h
@@ -186,7 +186,7 @@ RTEMS_INLINE_ROUTINE Status_Control _CORE_semaphore_Seize(
_Thread_queue_Context_set_thread_state(
queue_context,
- STATES_THREAD_QUEUE_WITH_IDENTIFIER | STATES_WAITING_FOR_SEMAPHORE
+ STATES_WAITING_FOR_SEMAPHORE
);
_Thread_queue_Context_set_do_nothing_enqueue_callout( queue_context );
_Thread_queue_Enqueue(
diff --git a/cpukit/score/include/rtems/score/mrspimpl.h b/cpukit/score/include/rtems/score/mrspimpl.h
index 9773d4a01e..b9514160dc 100644
--- a/cpukit/score/include/rtems/score/mrspimpl.h
+++ b/cpukit/score/include/rtems/score/mrspimpl.h
@@ -223,7 +223,7 @@ RTEMS_INLINE_ROUTINE Status_Control _MRSP_Initialize(
}
}
- _Thread_queue_Initialize( &mrsp->Wait_queue );
+ _Thread_queue_Object_initialize( &mrsp->Wait_queue );
return STATUS_SUCCESSFUL;
}
diff --git a/cpukit/score/include/rtems/score/statesimpl.h b/cpukit/score/include/rtems/score/statesimpl.h
index 6aa09470e7..db462fbb9a 100644
--- a/cpukit/score/include/rtems/score/statesimpl.h
+++ b/cpukit/score/include/rtems/score/statesimpl.h
@@ -41,71 +41,62 @@ extern "C" {
/** This macro corresponds to a task being ready. */
#define STATES_READY 0x00000000
-/**
- * @brief This macro corresponds to a task which is blocked on a thread queue
- * embedded in an object with an identifier.
- *
- * This thread state bit is intended to ease debugging and improve system
- * diagnostics, see _Thread_Wait_get_id().
- */
-#define STATES_THREAD_QUEUE_WITH_IDENTIFIER 0x00000001
-
/** This macro corresponds to a task waiting for a mutex. */
-#define STATES_WAITING_FOR_MUTEX 0x00000002
+#define STATES_WAITING_FOR_MUTEX 0x00000001
/** This macro corresponds to a task waiting for a semaphore. */
-#define STATES_WAITING_FOR_SEMAPHORE 0x00000004
+#define STATES_WAITING_FOR_SEMAPHORE 0x00000002
/** This macro corresponds to a task waiting for an event. */
-#define STATES_WAITING_FOR_EVENT 0x00000008
+#define STATES_WAITING_FOR_EVENT 0x00000004
/** This macro corresponds to a task waiting for a system event. */
-#define STATES_WAITING_FOR_SYSTEM_EVENT 0x00000010
+#define STATES_WAITING_FOR_SYSTEM_EVENT 0x00000008
/** This macro corresponds to a task waiting for a message. */
-#define STATES_WAITING_FOR_MESSAGE 0x00000020
+#define STATES_WAITING_FOR_MESSAGE 0x00000010
/** This macro corresponds to a task waiting for a condition variable. */
-#define STATES_WAITING_FOR_CONDITION_VARIABLE 0x00000040
+#define STATES_WAITING_FOR_CONDITION_VARIABLE 0x00000020
/** This macro corresponds to a task waiting for a futex. */
-#define STATES_WAITING_FOR_FUTEX 0x00000080
+#define STATES_WAITING_FOR_FUTEX 0x00000040
/** This macro corresponds to a task waiting for BSD wakeup. */
-#define STATES_WAITING_FOR_BSD_WAKEUP 0x00000100
+#define STATES_WAITING_FOR_BSD_WAKEUP 0x00000080
/**
* @brief This macro corresponds to a task which is waiting for a relative or
* absolute timeout.
*/
-#define STATES_WAITING_FOR_TIME 0x00000200
+#define STATES_WAITING_FOR_TIME 0x00000100
/** This macro corresponds to a task waiting for a period. */
-#define STATES_WAITING_FOR_PERIOD 0x00000400
+#define STATES_WAITING_FOR_PERIOD 0x00000200
/** This macro corresponds to a task waiting for a signal. */
-#define STATES_WAITING_FOR_SIGNAL 0x00000800
+#define STATES_WAITING_FOR_SIGNAL 0x00000400
/** This macro corresponds to a task waiting for a barrier. */
-#define STATES_WAITING_FOR_BARRIER 0x00001000
+#define STATES_WAITING_FOR_BARRIER 0x00000800
/** This macro corresponds to a task waiting for a RWLock. */
-#define STATES_WAITING_FOR_RWLOCK 0x00002000
+#define STATES_WAITING_FOR_RWLOCK 0x00001000
/** This macro corresponds to a task waiting for a join while exiting. */
-#define STATES_WAITING_FOR_JOIN_AT_EXIT 0x00004000
+#define STATES_WAITING_FOR_JOIN_AT_EXIT 0x00002000
/** This macro corresponds to a task waiting for a join. */
-#define STATES_WAITING_FOR_JOIN 0x00008000
+#define STATES_WAITING_FOR_JOIN 0x00004000
/** This macro corresponds to a task being suspended. */
-#define STATES_SUSPENDED 0x00010000
+#define STATES_SUSPENDED 0x00008000
/** This macro corresponds to a task waiting for a fixed size segment. */
-#define STATES_WAITING_FOR_SEGMENT 0x00020000
+#define STATES_WAITING_FOR_SEGMENT 0x00010000
/** This macro corresponds to a task those life is changing. */
-#define STATES_LIFE_IS_CHANGING 0x00040000
+#define STATES_LIFE_IS_CHANGING 0x00020000
/** This macro corresponds to a task being held by the debugger. */
#define STATES_DEBUGGER 0x08000000
@@ -125,8 +116,7 @@ extern "C" {
#define STATES_DORMANT 0x80000000
/** This macro corresponds to a task waiting for a local object operation. */
-#define STATES_LOCALLY_BLOCKED ( STATES_THREAD_QUEUE_WITH_IDENTIFIER | \
- STATES_WAITING_FOR_SEGMENT | \
+#define STATES_LOCALLY_BLOCKED ( STATES_WAITING_FOR_SEGMENT | \
STATES_WAITING_FOR_MESSAGE | \
STATES_WAITING_FOR_SEMAPHORE | \
STATES_WAITING_FOR_MUTEX | \
diff --git a/cpukit/score/include/rtems/score/threadqimpl.h b/cpukit/score/include/rtems/score/threadqimpl.h
index 25a3f2d9f1..574221ca9c 100644
--- a/cpukit/score/include/rtems/score/threadqimpl.h
+++ b/cpukit/score/include/rtems/score/threadqimpl.h
@@ -353,7 +353,8 @@ RTEMS_INLINE_ROUTINE void _Thread_queue_Heads_initialize(
}
RTEMS_INLINE_ROUTINE void _Thread_queue_Queue_initialize(
- Thread_queue_Queue *queue
+ Thread_queue_Queue *queue,
+ const char *name
)
{
#if defined(RTEMS_SMP)
@@ -361,6 +362,7 @@ RTEMS_INLINE_ROUTINE void _Thread_queue_Queue_initialize(
#endif
queue->heads = NULL;
queue->owner = NULL;
+ queue->name = name;
}
RTEMS_INLINE_ROUTINE void _Thread_queue_Queue_do_acquire_critical(
@@ -418,6 +420,25 @@ RTEMS_INLINE_ROUTINE void _Thread_queue_Queue_release(
_ISR_lock_ISR_enable( lock_context );
}
+/**
+ * @brief Copies the thread queue name to the specified buffer.
+ *
+ * @param[in] queue The actual thread queue.
+ * @param[in] buffer The buffer for the thread queue name copy.
+ * @param[in] buffer_size The buffer size in characters.
+ * @param[in] id The object identifier in case the thread queue is embedded in
+ * an object with identifier, otherwise it is set to 0.
+ *
+ * @retval The length of the thread queue name. May be greater than or equal
+ * to the buffer size if truncation occurred.
+ */
+size_t _Thread_queue_Queue_get_name_and_id(
+ const Thread_queue_Queue *queue,
+ char *buffer,
+ size_t buffer_size,
+ Objects_Id *id
+);
+
#if defined(RTEMS_SMP)
void _Thread_queue_Do_acquire_critical(
Thread_queue_Control *the_thread_queue,
@@ -1011,7 +1032,10 @@ size_t _Thread_queue_Flush_critical(
Thread_queue_Context *queue_context
);
-void _Thread_queue_Initialize( Thread_queue_Control *the_thread_queue );
+void _Thread_queue_Initialize(
+ Thread_queue_Control *the_thread_queue,
+ const char *name
+);
#if defined(RTEMS_SMP) && defined(RTEMS_DEBUG) && defined(RTEMS_PROFILING)
#define THREAD_QUEUE_INITIALIZER( _name ) \
@@ -1137,6 +1161,26 @@ extern const Thread_queue_Operations _Thread_queue_Operations_priority;
extern const Thread_queue_Operations _Thread_queue_Operations_priority_inherit;
+/**
+ * @brief The special thread queue name to indicated that the thread queue is
+ * embedded in an object with identifier.
+ *
+ * @see _Thread_queue_Object_initialize().
+ */
+extern const char _Thread_queue_Object_name[];
+
+/**
+ * @brief Initializes a thread queue embedded in an object with identifier.
+ *
+ * The object must have the layout specified by Thread_queue_Object. It should
+ * be ensured with the THREAD_QUEUE_OBJECT_ASSERT() static assertion.
+ *
+ * @param[in] the_thread_queue The thread queue.
+ */
+void _Thread_queue_Object_initialize(
+ Thread_queue_Control *the_thread_queue
+);
+
/**@}*/
#ifdef __cplusplus
diff --git a/cpukit/score/src/corebarrier.c b/cpukit/score/src/corebarrier.c
index 8da3ca1063..98dfd0bb4e 100644
--- a/cpukit/score/src/corebarrier.c
+++ b/cpukit/score/src/corebarrier.c
@@ -29,5 +29,5 @@ void _CORE_barrier_Initialize(
the_barrier->Attributes = *the_barrier_attributes;
the_barrier->number_of_waiting_threads = 0;
- _Thread_queue_Initialize( &the_barrier->Wait_queue );
+ _Thread_queue_Object_initialize( &the_barrier->Wait_queue );
}
diff --git a/cpukit/score/src/corebarrierwait.c b/cpukit/score/src/corebarrierwait.c
index 9cce4cfa56..f47c039e7b 100644
--- a/cpukit/score/src/corebarrierwait.c
+++ b/cpukit/score/src/corebarrierwait.c
@@ -46,7 +46,7 @@ Status_Control _CORE_barrier_Seize(
the_barrier->number_of_waiting_threads = number_of_waiting_threads;
_Thread_queue_Context_set_thread_state(
queue_context,
- STATES_THREAD_QUEUE_WITH_IDENTIFIER | STATES_WAITING_FOR_BARRIER
+ STATES_WAITING_FOR_BARRIER
);
_Thread_queue_Context_set_do_nothing_enqueue_callout( queue_context );
_Thread_queue_Enqueue(
diff --git a/cpukit/score/src/coremsg.c b/cpukit/score/src/coremsg.c
index a3a0d76089..1dbadc25c7 100644
--- a/cpukit/score/src/coremsg.c
+++ b/cpukit/score/src/coremsg.c
@@ -104,7 +104,7 @@ bool _CORE_message_queue_Initialize(
_Chain_Initialize_empty( &the_message_queue->Pending_messages );
- _Thread_queue_Initialize( &the_message_queue->Wait_queue );
+ _Thread_queue_Object_initialize( &the_message_queue->Wait_queue );
if ( discipline == CORE_MESSAGE_QUEUE_DISCIPLINES_PRIORITY ) {
the_message_queue->operations = &_Thread_queue_Operations_priority;
diff --git a/cpukit/score/src/coremsgseize.c b/cpukit/score/src/coremsgseize.c
index 98a7b156d4..b48a3f93d7 100644
--- a/cpukit/score/src/coremsgseize.c
+++ b/cpukit/score/src/coremsgseize.c
@@ -115,7 +115,7 @@ Status_Control _CORE_message_queue_Seize(
_Thread_queue_Context_set_thread_state(
queue_context,
- STATES_THREAD_QUEUE_WITH_IDENTIFIER | STATES_WAITING_FOR_MESSAGE
+ STATES_WAITING_FOR_MESSAGE
);
_Thread_queue_Context_set_do_nothing_enqueue_callout( queue_context );
_Thread_queue_Enqueue(
diff --git a/cpukit/score/src/coremsgsubmit.c b/cpukit/score/src/coremsgsubmit.c
index 5a8827a843..3c961014e3 100644
--- a/cpukit/score/src/coremsgsubmit.c
+++ b/cpukit/score/src/coremsgsubmit.c
@@ -133,7 +133,7 @@ Status_Control _CORE_message_queue_Submit(
_Thread_queue_Context_set_thread_state(
queue_context,
- STATES_THREAD_QUEUE_WITH_IDENTIFIER | STATES_WAITING_FOR_MESSAGE
+ STATES_WAITING_FOR_MESSAGE
);
_Thread_queue_Context_set_do_nothing_enqueue_callout( queue_context );
_Thread_queue_Enqueue(
diff --git a/cpukit/score/src/coremutexseize.c b/cpukit/score/src/coremutexseize.c
index b00cad9707..4309380627 100644
--- a/cpukit/score/src/coremutexseize.c
+++ b/cpukit/score/src/coremutexseize.c
@@ -34,7 +34,7 @@ Status_Control _CORE_mutex_Seize_slow(
if ( wait ) {
_Thread_queue_Context_set_thread_state(
queue_context,
- STATES_THREAD_QUEUE_WITH_IDENTIFIER | STATES_WAITING_FOR_MUTEX
+ STATES_WAITING_FOR_MUTEX
);
_Thread_queue_Context_set_do_nothing_enqueue_callout( queue_context );
_Thread_queue_Context_set_deadlock_callout(
diff --git a/cpukit/score/src/corerwlock.c b/cpukit/score/src/corerwlock.c
index eae62584e9..51ae2c70df 100644
--- a/cpukit/score/src/corerwlock.c
+++ b/cpukit/score/src/corerwlock.c
@@ -28,5 +28,5 @@ void _CORE_RWLock_Initialize(
the_rwlock->number_of_readers = 0;
the_rwlock->current_state = CORE_RWLOCK_UNLOCKED;
- _Thread_queue_Initialize( &the_rwlock->Wait_queue );
+ _Thread_queue_Object_initialize( &the_rwlock->Wait_queue );
}
diff --git a/cpukit/score/src/corerwlockobtainread.c b/cpukit/score/src/corerwlockobtainread.c
index 035b1d59ae..641945635f 100644
--- a/cpukit/score/src/corerwlockobtainread.c
+++ b/cpukit/score/src/corerwlockobtainread.c
@@ -80,7 +80,7 @@ Status_Control _CORE_RWLock_Seize_for_reading(
_Thread_queue_Context_set_thread_state(
queue_context,
- STATES_THREAD_QUEUE_WITH_IDENTIFIER | STATES_WAITING_FOR_RWLOCK
+ STATES_WAITING_FOR_RWLOCK
);
_Thread_queue_Context_set_do_nothing_enqueue_callout( queue_context );
_Thread_queue_Enqueue(
diff --git a/cpukit/score/src/corerwlockobtainwrite.c b/cpukit/score/src/corerwlockobtainwrite.c
index 75168fc062..7f636daa99 100644
--- a/cpukit/score/src/corerwlockobtainwrite.c
+++ b/cpukit/score/src/corerwlockobtainwrite.c
@@ -68,7 +68,7 @@ Status_Control _CORE_RWLock_Seize_for_writing(
_Thread_queue_Context_set_thread_state(
queue_context,
- STATES_THREAD_QUEUE_WITH_IDENTIFIER | STATES_WAITING_FOR_RWLOCK
+ STATES_WAITING_FOR_RWLOCK
);
_Thread_queue_Context_set_do_nothing_enqueue_callout( queue_context );
_Thread_queue_Enqueue(
diff --git a/cpukit/score/src/coresem.c b/cpukit/score/src/coresem.c
index e928f3d3a5..f8966448e3 100644
--- a/cpukit/score/src/coresem.c
+++ b/cpukit/score/src/coresem.c
@@ -27,5 +27,5 @@ void _CORE_semaphore_Initialize(
{
the_semaphore->count = initial_value;
- _Thread_queue_Initialize( &the_semaphore->Wait_queue );
+ _Thread_queue_Object_initialize( &the_semaphore->Wait_queue );
}
diff --git a/cpukit/score/src/threadinitialize.c b/cpukit/score/src/threadinitialize.c
index 60bbd22238..2eb6507565 100644
--- a/cpukit/score/src/threadinitialize.c
+++ b/cpukit/score/src/threadinitialize.c
@@ -250,13 +250,12 @@ bool _Thread_Initialize(
_Thread_queue_Gate_open( &the_thread->Wait.Lock.Tranquilizer );
_RBTree_Initialize_node( &the_thread->Wait.Link.Registry_node );
_SMP_lock_Stats_initialize( &the_thread->Potpourri_stats, "Thread Potpourri" );
+ _SMP_lock_Stats_initialize( &the_thread->Join_queue.Lock_stats, "Thread State" );
#endif
/* Initialize the CPU for the non-SMP schedulers */
_Thread_Set_CPU( the_thread, cpu );
- _Thread_queue_Initialize( &the_thread->Join_queue );
-
the_thread->current_state = STATES_DORMANT;
the_thread->Wait.operations = &_Thread_queue_Operations_default;
the_thread->Start.initial_priority = priority;
diff --git a/cpukit/score/src/threadq.c b/cpukit/score/src/threadq.c
index baed6325da..850ec6f07b 100644
--- a/cpukit/score/src/threadq.c
+++ b/cpukit/score/src/threadq.c
@@ -130,14 +130,24 @@ void _Thread_queue_Release(
}
#endif
-void _Thread_queue_Initialize( Thread_queue_Control *the_thread_queue )
+const char _Thread_queue_Object_name[] = { '\0' };
+
+void _Thread_queue_Initialize(
+ Thread_queue_Control *the_thread_queue,
+ const char *name
+)
{
- _Thread_queue_Queue_initialize( &the_thread_queue->Queue );
+ _Thread_queue_Queue_initialize( &the_thread_queue->Queue, name );
#if defined(RTEMS_SMP)
_SMP_lock_Stats_initialize( &the_thread_queue->Lock_stats, "Thread Queue" );
#endif
}
+void _Thread_queue_Object_initialize( Thread_queue_Control *the_thread_queue )
+{
+ _Thread_queue_Initialize( the_thread_queue, _Thread_queue_Object_name );
+}
+
#if defined(RTEMS_MULTIPROCESSING)
void _Thread_queue_MP_callout_do_nothing(
Thread_Control *the_proxy,
@@ -147,3 +157,35 @@ void _Thread_queue_MP_callout_do_nothing(
/* Do nothing */
}
#endif
+
+size_t _Thread_queue_Queue_get_name_and_id(
+ const Thread_queue_Queue *queue,
+ char *buffer,
+ size_t buffer_size,
+ Objects_Id *id
+)
+{
+ const char *name;
+
+ name = queue->name;
+
+ if ( name == _Thread_queue_Object_name ) {
+ const Thread_queue_Object *queue_object;
+
+ queue_object = THREAD_QUEUE_QUEUE_TO_OBJECT( queue );
+ *id = queue_object->Object.id;
+ return _Objects_Name_to_string(
+ queue_object->Object.name,
+ false,
+ buffer,
+ buffer_size
+ );
+ } else {
+ if ( name == NULL ) {
+ name = _Thread_queue_Object_name;
+ }
+
+ *id = 0;
+ return strlcpy( buffer, name, buffer_size );
+ }
+}
diff --git a/cpukit/score/src/threadwaitgetid.c b/cpukit/score/src/threadwaitgetid.c
index 9f17250353..fd32e5ec84 100644
--- a/cpukit/score/src/threadwaitgetid.c
+++ b/cpukit/score/src/threadwaitgetid.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016 embedded brains GmbH. All rights reserved.
+ * Copyright (c) 2016, 2017 embedded brains GmbH. All rights reserved.
*
* embedded brains GmbH
* Dornierstr. 4
@@ -20,21 +20,20 @@
Objects_Id _Thread_Wait_get_id( const Thread_Control *the_thread )
{
- States_Control current_state;
-
- current_state = the_thread->current_state;
+ const Thread_queue_Queue *queue;
#if defined(RTEMS_MULTIPROCESSING)
- if ( ( current_state & STATES_WAITING_FOR_RPC_REPLY ) != 0 ) {
+ if ( _States_Is_waiting_for_rpc_reply( the_thread->current_state ) ) {
return the_thread->Wait.remote_id;
}
#endif
- if ( ( current_state & STATES_THREAD_QUEUE_WITH_IDENTIFIER ) != 0 ) {
- const Thread_queue_Object *queue_object;
+ queue = the_thread->Wait.queue;
- queue_object = THREAD_QUEUE_QUEUE_TO_OBJECT( the_thread->Wait.queue );
+ if ( queue != NULL && queue->name == _Thread_queue_Object_name ) {
+ const Thread_queue_Object *queue_object;
+ queue_object = THREAD_QUEUE_QUEUE_TO_OBJECT( queue );
return queue_object->Object.id;
}