Added Thread abstract class 27/49927/5
authorXiangyin Ma <x1.ma@samsung.com>
Wed, 21 Oct 2015 15:14:35 +0000 (16:14 +0100)
committerXiangyin Ma <x1.ma@samsung.com>
Thu, 22 Oct 2015 16:53:23 +0000 (17:53 +0100)
Change-Id: Id9be6082ef416bf2ea619bd3be7327a4f2c46f73

15 files changed:
automated-tests/src/dali-devel/CMakeLists.txt
automated-tests/src/dali-devel/utc-Dali-ConditionalWait.cpp
automated-tests/src/dali-devel/utc-Dali-Mutex.cpp
automated-tests/src/dali-devel/utc-Dali-Thread.cpp [new file with mode: 0644]
build/tizen/dali-core/Makefile.am
dali/devel-api/file.list
dali/devel-api/threading/conditional-wait.cpp [moved from dali/devel-api/common/conditional-wait.cpp with 98% similarity]
dali/devel-api/threading/conditional-wait.h [moved from dali/devel-api/common/conditional-wait.h with 100% similarity]
dali/devel-api/threading/mutex.cpp [moved from dali/devel-api/common/mutex.cpp with 97% similarity]
dali/devel-api/threading/mutex.h [moved from dali/devel-api/common/mutex.h with 100% similarity]
dali/devel-api/threading/thread.cpp [new file with mode: 0644]
dali/devel-api/threading/thread.h [new file with mode: 0644]
dali/internal/event/common/notification-manager.cpp
dali/internal/update/manager/update-manager.cpp
dali/internal/update/queue/update-message-queue.cpp

index 96fa090..e291f77 100644 (file)
@@ -24,6 +24,7 @@ SET(TC_SOURCES
         utc-Dali-SignalDelegate.cpp
         utc-Dali-Scripting.cpp
         utc-Dali-Shader.cpp
+        utc-Dali-Thread.cpp
         utc-Dali-WeakHandle.cpp
 )
 
index 7946c8a..44b85bd 100644 (file)
 #include <stdlib.h>
 #include <unistd.h>
 #include <dali-test-suite-utils.h>
-#include <dali/devel-api/common/conditional-wait.h>
+#include <dali/devel-api/threading/conditional-wait.h>
+#include <dali/devel-api/threading/thread.h>
 
 using Dali::ConditionalWait;
+using Dali::Thread;
 
 namespace // for local variables to avoid name clashes
 {
@@ -29,35 +31,59 @@ volatile int gGlobalValue = 0;
 volatile bool gWorkerThreadWait = true;
 enum ThreadState { INIT, RUN, TERMINATE } volatile gWorkerThreadState = INIT;
 ConditionalWait* volatile gConditionalWait; // volatile pointer to a ConditionalWait object
-}
 
-void* WorkerThreadNotify( void* ptr )
+class WorkerThreadNotify : public Thread
 {
-  gGlobalValue = -1;
-  while( gWorkerThreadWait ) // wait till we can exit
+  virtual void Run()
   {
-    gWorkerThreadState = RUN;
-    usleep( 1 ); // 1 microseconds
+    gGlobalValue = -1;
+    while( gWorkerThreadWait ) // wait till we can exit
+    {
+      gWorkerThreadState = RUN;
+      usleep( 1 ); // 1 microseconds
+    }
+    usleep( 200 ); // give other thread time to get to Wait
+    gGlobalValue = 1;
+    gConditionalWait->Notify();
+    gWorkerThreadState = TERMINATE;
   }
-  usleep( 200 ); // give other thread time to get to Wait
-  gGlobalValue = 1;
-  gConditionalWait->Notify();
-  gWorkerThreadState = TERMINATE;
-  return NULL;
+};
+
+volatile int gNotifyCount = 0;
+class WorkerThreadNotifyN : public Thread
+{
+  virtual void Run()
+  {
+    while( gNotifyCount > 0 )
+    {
+      gConditionalWait->Notify();
+      usleep( 10 ); // 10 microseconds between each notify
+    }
+  }
+};
+
+class WorkerThreadWaitN : public Thread
+{
+  virtual void Run()
+  {
+    gConditionalWait->Wait();
+  }
+};
+
 }
 
 int UtcConditionalWait1P(void)
 {
   tet_infoline("Testing ConditionalWait - scenario:  wait - notify with 2 threads");
 
-  pthread_t thread1;
+  WorkerThreadNotify thread1;
   // initialize values
   gConditionalWait = new ConditionalWait();
   gWorkerThreadWait = true;
   DALI_TEST_EQUALS( INIT, gWorkerThreadState, TEST_LOCATION );
   DALI_TEST_EQUALS( 0, gGlobalValue, TEST_LOCATION );
 
-  pthread_create( &thread1, NULL, &WorkerThreadNotify, NULL );
+  thread1.Start();
   // wait till the thread is in run state
   while( RUN != gWorkerThreadState )
   {
@@ -75,8 +101,7 @@ int UtcConditionalWait1P(void)
     usleep( 1 ); // 1 microsecond
   }
 
-  void* exitValue;
-  pthread_join( thread1, &exitValue );
+  thread1.Join();
 
   delete gConditionalWait;
   END_TEST;
@@ -94,16 +119,6 @@ int UtcConditionalWait2P(void)
   END_TEST;
 }
 
-volatile int gNotifyCount = 0;
-void* WorkerThreadNotifyN( void* ptr )
-{
-  while( gNotifyCount > 0 )
-  {
-    gConditionalWait->Notify();
-    usleep( 10 ); // 10 microseconds between each notify
-  }
-  return NULL;
-}
 
 int UtcConditionalWait3P(void)
 {
@@ -113,8 +128,8 @@ int UtcConditionalWait3P(void)
   gConditionalWait = new ConditionalWait();
   gNotifyCount = 100;
 
-  pthread_t thread1;
-  pthread_create( &thread1, NULL, &WorkerThreadNotifyN, NULL );
+  WorkerThreadNotifyN thread1;
+  thread1.Start();
 
   while( gNotifyCount > 0 )
   {
@@ -125,8 +140,7 @@ int UtcConditionalWait3P(void)
   }
   DALI_TEST_EQUALS( 0u, gConditionalWait->GetWaitCount(), TEST_LOCATION );
 
-  void* exitValue;
-  pthread_join( thread1, &exitValue );
+  thread1.Join();
 
   delete gConditionalWait;
   END_TEST;
@@ -140,12 +154,12 @@ int UtcConditionalWait4P(void)
   gConditionalWait = new ConditionalWait();
   gNotifyCount = 100;
 
-  pthread_t thread1;
-  pthread_create( &thread1, NULL, &WorkerThreadNotifyN, NULL );
-  pthread_t thread2;
-  pthread_create( &thread2, NULL, &WorkerThreadNotifyN, NULL );
-  pthread_t thread3;
-  pthread_create( &thread3, NULL, &WorkerThreadNotifyN, NULL );
+  WorkerThreadNotifyN thread1;
+  thread1.Start();
+  WorkerThreadNotifyN thread2;
+  thread2.Start();
+  WorkerThreadNotifyN thread3;
+  thread3.Start();
 
   while( gNotifyCount > 0 )
   {
@@ -155,22 +169,14 @@ int UtcConditionalWait4P(void)
     usleep( 10 ); // 10 microseconds between each notify
   }
 
-  void* exitValue;
-  pthread_join( thread1, &exitValue );
-  pthread_join( thread2, &exitValue );
-  pthread_join( thread3, &exitValue );
+  thread1.Join();
+  thread2.Join();
+  thread3.Join();
 
   delete gConditionalWait;
   END_TEST;
 }
 
-void* WorkerThreadWaitN( void* ptr )
-{
-  gConditionalWait->Wait();
-
-  return NULL;
-}
-
 int UtcConditionalWait5P(void)
 {
   tet_infoline("Testing ConditionalWait - scenario:  4 threads wait - notify once from 1 thread");
@@ -178,15 +184,14 @@ int UtcConditionalWait5P(void)
   // initialize values
   gConditionalWait = new ConditionalWait();
 
-  pthread_t thread1;
-  pthread_create( &thread1, NULL, &WorkerThreadWaitN, NULL );
-  pthread_t thread2;
-  pthread_create( &thread2, NULL, &WorkerThreadWaitN, NULL );
-  pthread_t thread3;
-  pthread_create( &thread3, NULL, &WorkerThreadWaitN, NULL );
-  pthread_t thread4;
-  pthread_create( &thread4, NULL, &WorkerThreadWaitN, NULL );
-
+  WorkerThreadWaitN thread1;
+  thread1.Start();
+  WorkerThreadWaitN thread2;
+  thread2.Start();
+  WorkerThreadWaitN thread3;
+  thread3.Start();
+  WorkerThreadWaitN thread4;
+  thread4.Start();
   // wait till all child threads are waiting
   while( gConditionalWait->GetWaitCount() < 4 )
   { }
@@ -194,11 +199,10 @@ int UtcConditionalWait5P(void)
   // notify once, it will resume all threads
   gConditionalWait->Notify();
 
-  void* exitValue;
-  pthread_join( thread1, &exitValue );
-  pthread_join( thread2, &exitValue );
-  pthread_join( thread3, &exitValue );
-  pthread_join( thread4, &exitValue );
+  thread1.Join();
+  thread2.Join();
+  thread3.Join();
+  thread4.Join();
 
   DALI_TEST_EQUALS( 0u, gConditionalWait->GetWaitCount(), TEST_LOCATION );
 
index 290a826..8a1a5b0 100644 (file)
 #include <stdlib.h>
 #include <unistd.h>
 #include <dali/public-api/dali-core.h>
-#include <dali/devel-api/common/mutex.h>
+#include <dali/devel-api/threading/mutex.h>
+#include <dali/devel-api/threading/thread.h>
 #include <dali-test-suite-utils.h>
 
 using Dali::Mutex;
+using Dali::Thread;
 
 int UtcDaliMutexSingleThread(void)
 {
@@ -55,21 +57,24 @@ volatile int gGlobalValue = 0;
 volatile bool gWorkerThreadWait = true;
 volatile enum ThreadState { INIT, RUN, LOCKING, TERMINATE } gWorkerThreadState = INIT;
 Mutex* volatile gGlobalValueMutex; // volatile pointer to a mutex object
-}
-void* WorkerThread1( void* ptr )
+
+class TestThread : public Thread
 {
-  gWorkerThreadState = RUN;
+  virtual void Run()
   {
-    Mutex::ScopedLock lock( *gGlobalValueMutex );
-    gWorkerThreadState = LOCKING;
-    gGlobalValue = -1;
-    while( gWorkerThreadWait ) // wait till we can exit
+    gWorkerThreadState = RUN;
     {
-      usleep( 1 ); // 1 microsecond
+      Mutex::ScopedLock lock( *gGlobalValueMutex );
+      gWorkerThreadState = LOCKING;
+      gGlobalValue = -1;
+      while( gWorkerThreadWait ) // wait till we can exit
+      {
+        usleep( 1 ); // 1 microsecond
+      }
     }
+    gWorkerThreadState = TERMINATE;
   }
-  gWorkerThreadState = TERMINATE;
-  return NULL;
+};
 }
 
 int UtcDaliMutexMultiThread(void)
@@ -78,7 +83,7 @@ int UtcDaliMutexMultiThread(void)
 
   gGlobalValueMutex = new Dali::Mutex();
 
-  pthread_t thread1;
+  TestThread thread1;
   // initialize values
   gGlobalValue = 0;
   gWorkerThreadWait = true;
@@ -90,7 +95,7 @@ int UtcDaliMutexMultiThread(void)
   {
     Mutex::ScopedLock lock( *gGlobalValueMutex );
     DALI_TEST_EQUALS( true, gGlobalValueMutex->IsLocked(), TEST_LOCATION );
-    pthread_create( &thread1, NULL, &WorkerThread1, NULL );
+    thread1.Start();
     // wait till the thread is in run state
     while( RUN != gWorkerThreadState )
     {
@@ -119,8 +124,7 @@ int UtcDaliMutexMultiThread(void)
     usleep( 1 ); // 1 microsecond
   }
   DALI_TEST_EQUALS( false, gGlobalValueMutex->IsLocked(), TEST_LOCATION );
-  void* exitValue;
-  pthread_join( thread1, &exitValue );
+  thread1.Join();
 
   END_TEST;
 }
diff --git a/automated-tests/src/dali-devel/utc-Dali-Thread.cpp b/automated-tests/src/dali-devel/utc-Dali-Thread.cpp
new file mode 100644 (file)
index 0000000..a62fde7
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <iostream>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dali-test-suite-utils.h>
+#include <dali/devel-api/threading/thread.h>
+
+using Dali::Thread;
+
+namespace
+{
+volatile bool gRunThreadEntryFunc = false;
+
+class TestThread : public Thread
+{
+  virtual void Run()
+  {
+    gRunThreadEntryFunc = true;
+  }
+};
+}
+
+int UtcDaliThreadP(void)
+{
+  tet_infoline("Testing Dali::Thread");
+
+  gRunThreadEntryFunc = false;
+
+  TestThread thread;
+
+  thread.Start();
+  // wait till the thread is terminated
+  while( !gRunThreadEntryFunc )
+  {
+    usleep( 1 ); // 1 microsecond
+  }
+  DALI_TEST_EQUALS( true, gRunThreadEntryFunc, TEST_LOCATION );
+
+  thread.Join();
+
+  // Restart the thread after joined
+  gRunThreadEntryFunc = false;
+  thread.Start();
+  thread.Join();
+  // wait till the thread is terminated
+  while( !gRunThreadEntryFunc )
+  {
+    usleep( 1 ); // 1 microsecond
+  }
+  DALI_TEST_EQUALS( true, gRunThreadEntryFunc, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int UtcDaliThreadNonCopyable(void)
+{
+  // we want to make sure that mutex is not copyable (its copy constructor is not defined)
+  // this test will stop compiling if Mutex has compiler generated copy constructor
+  DALI_COMPILE_TIME_ASSERT( !__has_trivial_copy( Thread ) );
+
+  DALI_TEST_CHECK( true );
+  END_TEST;
+}
index 823fc93..82f50d7 100644 (file)
@@ -94,6 +94,7 @@ develapiobjectdir = $(develapidir)/object
 develapirenderingdir = $(develapidir)/rendering
 develapiscriptingdir = $(develapidir)/scripting
 develapisignalsdir = $(develapidir)/signals
+develapithreadingdir = $(develapidir)/threading
 
 develapi_HEADERS = $(devel_api_header_files)
 develapiactors_HEADERS = $(devel_api_core_actors_header_files)
@@ -106,6 +107,7 @@ develapiobject_HEADERS = $(devel_api_core_object_header_files)
 develapirendering_HEADERS = $(devel_api_core_rendering_header_files)
 develapiscripting_HEADERS = $(devel_api_core_scripting_header_files)
 develapisignals_HEADERS = $(devel_api_core_signals_header_files)
+develapithreading_HEADERS = $(devel_api_core_threading_header_files)
 
 
 #public api
index 4c907e2..7528fe1 100644 (file)
@@ -3,9 +3,7 @@
 devel_api_src_files = \
   $(devel_api_src_dir)/animation/animation-data.cpp \
   $(devel_api_src_dir)/animation/path-constrainer.cpp \
-  $(devel_api_src_dir)/common/conditional-wait.cpp \
   $(devel_api_src_dir)/common/hash.cpp \
-  $(devel_api_src_dir)/common/mutex.cpp \
   $(devel_api_src_dir)/events/hit-test-algorithm.cpp \
   $(devel_api_src_dir)/images/atlas.cpp \
   $(devel_api_src_dir)/images/distance-field.cpp \
@@ -18,7 +16,10 @@ devel_api_src_files = \
   $(devel_api_src_dir)/rendering/sampler.cpp \
   $(devel_api_src_dir)/rendering/shader.cpp \
   $(devel_api_src_dir)/scripting/scripting.cpp \
-  $(devel_api_src_dir)/signals/signal-delegate.cpp
+  $(devel_api_src_dir)/signals/signal-delegate.cpp \
+  $(devel_api_src_dir)/threading/conditional-wait.cpp \
+  $(devel_api_src_dir)/threading/mutex.cpp \
+  $(devel_api_src_dir)/threading/thread.cpp
 
 # Add devel header files here DALi internal developer files used by Adaptor & Toolkit
 
@@ -29,11 +30,9 @@ devel_api_core_animation_header_files = \
 devel_api_core_common_header_files = \
   $(devel_api_src_dir)/common/hash.h \
   $(devel_api_src_dir)/common/map-wrapper.h \
-  $(devel_api_src_dir)/common/conditional-wait.h \
-  $(devel_api_src_dir)/common/mutex.h \
   $(devel_api_src_dir)/common/owner-container.h \
   $(devel_api_src_dir)/common/ref-counted-dali-vector.h \
-    $(devel_api_src_dir)/common/set-wrapper.h
+  $(devel_api_src_dir)/common/set-wrapper.h
 
 devel_api_core_events_header_files = \
   $(devel_api_src_dir)/events/hit-test-algorithm.h
@@ -61,4 +60,8 @@ devel_api_core_signals_header_files = \
 devel_api_core_scripting_header_files = \
   $(devel_api_src_dir)/scripting/scripting.h
 
+devel_api_core_threading_header_files = \
+  $(devel_api_src_dir)/threading/conditional-wait.h \
+  $(devel_api_src_dir)/threading/mutex.h \
+  $(devel_api_src_dir)/threading/thread.h
 
similarity index 98%
rename from dali/devel-api/common/conditional-wait.cpp
rename to dali/devel-api/threading/conditional-wait.cpp
index c22bff3..39c7e6f 100644 (file)
@@ -16,7 +16,7 @@
  */
 
 // CLASS HEADER
-#include <dali/devel-api/common/conditional-wait.h>
+#include <dali/devel-api/threading/conditional-wait.h>
 
 // INTERNAL INCLUDES
 #include <dali/internal/common/mutex-impl.h>
similarity index 97%
rename from dali/devel-api/common/mutex.cpp
rename to dali/devel-api/threading/mutex.cpp
index 685506c..8309b3b 100644 (file)
@@ -16,7 +16,7 @@
  */
 
 // CLASS HEADER
-#include <dali/devel-api/common/mutex.h>
+#include <dali/devel-api/threading/mutex.h>
 
 // INTERNAL INCLUDES
 #include <dali/internal/common/mutex-impl.h>
diff --git a/dali/devel-api/threading/thread.cpp b/dali/devel-api/threading/thread.cpp
new file mode 100644 (file)
index 0000000..d7bfa0b
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali/devel-api/threading/thread.h>
+
+// EXTERNAL INCLUDES
+#include <cstddef>
+#include <pthread.h>
+#include <dali/integration-api/debug.h>
+
+namespace Dali
+{
+
+struct Thread::ThreadImpl
+{
+  pthread_t thread;
+  bool isCreated;
+};
+
+Thread::Thread()
+: mImpl( new ThreadImpl )
+{
+  mImpl->isCreated = false;
+}
+
+Thread::~Thread()
+{
+  delete mImpl;
+}
+
+void Thread::Start()
+{
+  DALI_ASSERT_DEBUG( !mImpl->isCreated );
+
+  int error = pthread_create( &(mImpl->thread), NULL, InternalThreadEntryFunc, this );
+  DALI_ASSERT_ALWAYS( !error && "Failed to create a new thread" );
+  mImpl->isCreated = true;
+}
+
+void Thread::Join()
+{
+  if( mImpl->isCreated )
+  {
+    mImpl->isCreated = false;
+    pthread_join( mImpl->thread, NULL );
+  }
+}
+
+void* Thread::InternalThreadEntryFunc( void* This )
+{
+  ( static_cast<Thread*>( This ) )->Run();
+  return NULL;
+}
+
+} // namespace Dali
diff --git a/dali/devel-api/threading/thread.h b/dali/devel-api/threading/thread.h
new file mode 100644 (file)
index 0000000..535e700
--- /dev/null
@@ -0,0 +1,86 @@
+#ifndef __DALI_THREAD_H__
+#define __DALI_THREAD_H__
+
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include <dali/public-api/common/dali-common.h>
+
+
+/**
+ * The top level DALi namespace
+ */
+namespace Dali
+{
+
+/*
+ * @brief Abstract class for thread functionality. Can be used for worker threads.
+ */
+class DALI_IMPORT_API Thread
+{
+public:
+
+  /**
+   * @brief Creates a new thread and make it executable.
+   */
+  void Start();
+
+  /**
+   * @brief Wait for thread termination.
+   */
+  void Join();
+
+protected:
+
+  /**
+   * @brief Constructor
+   */
+  Thread();
+
+  /**
+   * @brief Destructor, virtual as this is used as base class
+   */
+  virtual ~Thread();
+
+  /**
+   * The routine that the thread will execute once it is started.
+   */
+  virtual void Run() = 0;
+
+private:
+
+  /**
+   * Helper for the thread calling the entry function.
+   * @param[in] This A pointer to the current RenderThread object
+   */
+  static void* InternalThreadEntryFunc( void* This );
+
+  // Undefined
+  Thread( const Thread& );
+  // Undefined
+  const Thread& operator=( const Thread& );
+
+private:
+
+  struct ThreadImpl;
+  ThreadImpl* mImpl;
+};
+
+}
+
+#endif /* __DALI_THREAD_H__ */
index 0b113d1..71011ee 100644 (file)
@@ -20,8 +20,8 @@
 
 // INTERNAL INCLUDES
 #include <dali/public-api/common/dali-common.h>
-#include <dali/devel-api/common/mutex.h>
 #include <dali/devel-api/common/owner-container.h>
+#include <dali/devel-api/threading/mutex.h>
 #include <dali/internal/common/message.h>
 #include <dali/internal/event/common/property-notification-impl.h>
 #include <dali/internal/event/common/complete-notification-interface.h>
index cc4f379..41e7431 100644 (file)
@@ -21,8 +21,8 @@
 // INTERNAL INCLUDES
 #include <dali/public-api/common/stage.h>
 #include <dali/devel-api/common/set-wrapper.h>
-#include <dali/devel-api/common/mutex.h>
 #include <dali/devel-api/common/owner-container.h>
+#include <dali/devel-api/threading/mutex.h>
 
 #include <dali/integration-api/core.h>
 #include <dali/integration-api/render-controller.h>
index bf529a6..0e361f7 100644 (file)
@@ -20,7 +20,7 @@
 
 // INTERNAL INCLUDES
 #include <dali/public-api/common/vector-wrapper.h>
-#include <dali/devel-api/common/mutex.h>
+#include <dali/devel-api/threading/mutex.h>
 #include <dali/integration-api/render-controller.h>
 #include <dali/internal/common/message.h>
 #include <dali/internal/common/message-buffer.h>