1/*****************************************************************************
2
3  Licensed to Accellera Systems Initiative Inc. (Accellera) under one or
4  more contributor license agreements.  See the NOTICE file distributed
5  with this work for additional information regarding copyright ownership.
6  Accellera licenses this file to you under the Apache License, Version 2.0
7  (the "License"); you may not use this file except in compliance with the
8  License.  You may obtain a copy of the License at
9
10    http://www.apache.org/licenses/LICENSE-2.0
11
12  Unless required by applicable law or agreed to in writing, software
13  distributed under the License is distributed on an "AS IS" BASIS,
14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
15  implied.  See the License for the specific language governing
16  permissions and limitations under the License.
17
18 *****************************************************************************/
19
20/*****************************************************************************
21
22  sc_cor_pthread.cpp -- Coroutine implementation with pthreads.
23
24  Original Author: Andy Goodrich, Forte Design Systems, 2002-11-10
25
26 CHANGE LOG APPEARS AT THE END OF THE FILE
27 *****************************************************************************/
28
29#if !defined(_WIN32) && !defined(WIN32) && defined(SC_USE_PTHREADS)
30
31// ORDER OF THE INCLUDES AND namespace sc_core IS IMPORTANT!!!
32
33#include "sysc/kernel/sc_cor_pthread.h"
34#include "sysc/kernel/sc_simcontext.h"
35
36using namespace std;
37
38namespace sc_core {
39
40// MAKE SURE WE HAVE A NULL THAT WILL WORK:
41
42#if defined(__hpux)
43#   define PTHREAD_NULL cma_c_null
44#else  // !defined(__hpux)
45#   define PTHREAD_NULL NULL
46#endif // !defined(__hpux)
47
48#define DEBUGF \
49    if (0) std::cout << "sc_cor_pthread.cpp(" << __LINE__ << ") "
50
51// ----------------------------------------------------------------------------
52//  File static variables.
53//
54// (1) The thread creation mutex and the creation condition are used to
55//     suspend the thread creating another one until the created thread
56//     reaches its invoke_module_method. This allows us to get control of
57//     thread scheduling away from the pthread package.
58// ----------------------------------------------------------------------------
59
60static sc_cor_pthread* active_cor_p=0;   // Active co-routine.
61static pthread_cond_t  create_condition; // See note 1 above.
62static pthread_mutex_t create_mutex;     // See note 1 above.
63static sc_cor_pthread  main_cor;         // Main coroutine.
64
65
66// ----------------------------------------------------------------------------
67//  CLASS : sc_cor_pthread
68//
69//  Coroutine class implemented with Posix Threads.
70// ----------------------------------------------------------------------------
71
72// constructor
73
74sc_cor_pthread::sc_cor_pthread()
75    : m_cor_fn_arg( 0 ), m_pkg_p( 0 )
76{
77    DEBUGF << this << ": sc_cor_pthread::sc_cor_pthread()" << std::endl;
78    pthread_cond_init( &m_pt_condition, PTHREAD_NULL );
79    pthread_mutex_init( &m_mutex, PTHREAD_NULL );
80}
81
82
83// destructor
84
85sc_cor_pthread::~sc_cor_pthread()
86{
87    DEBUGF << this << ": sc_cor_pthread::~sc_cor_pthread()" << std::endl;
88	pthread_cond_destroy( &m_pt_condition);
89	pthread_mutex_destroy( &m_mutex );
90}
91
92
93// This static method is a Posix Threads helper callback and invokes a thread
94// for the first time. It performs some synchronization and then invokes the
95// actual sc_cor helper function.
96//     context_p -> thread to invoke module method of.
97// Result is 0 and ignored.
98
99void* sc_cor_pthread::invoke_module_method(void* context_p)
100{
101    sc_cor_pthread* p = (sc_cor_pthread*)context_p;
102    DEBUGF << p << ": sc_cor_pthread::invoke_module_method()" << std::endl;
103
104
105    // SUSPEND THE THREAD SO WE CAN GAIN CONTROL FROM THE PTHREAD PACKAGE:
106    //
107    // Since pthread_create schedules each thread behind our back for its
108    // initial execution we immediately suspend a newly created thread
109    // here so we can control when its execution will occur. We also wake
110    // up the main thread which is waiting for this thread to execute to this
111    // wait point.
112
113    pthread_mutex_lock( &create_mutex );
114	DEBUGF << p << ": child signalling main thread " << endl;
115    pthread_cond_signal( &create_condition );
116    pthread_mutex_lock( &p->m_mutex );
117    pthread_mutex_unlock( &create_mutex );
118    pthread_cond_wait( &p->m_pt_condition, &p->m_mutex );
119    pthread_mutex_unlock( &p->m_mutex );
120
121
122    // CALL THE SYSTEMC CODE THAT WILL ACTUALLY START THE THREAD OFF:
123
124    active_cor_p = p;
125    DEBUGF << p << ": about to invoke real method "
126	   << active_cor_p << std::endl;
127    (p->m_cor_fn)(p->m_cor_fn_arg);
128
129    return 0;
130}
131
132
133// ----------------------------------------------------------------------------
134//  CLASS : sc_cor_pkg_pthread
135//
136//  Coroutine package class implemented with Posix Threads.
137// ----------------------------------------------------------------------------
138
139int sc_cor_pkg_pthread::instance_count = 0;
140
141
142// constructor
143
144sc_cor_pkg_pthread::sc_cor_pkg_pthread( sc_simcontext* simc )
145: sc_cor_pkg( simc )
146{
147    // initialize the current coroutine
148    if( ++ instance_count == 1 )
149    {
150        pthread_cond_init( &create_condition, PTHREAD_NULL );
151        pthread_mutex_init( &create_mutex, PTHREAD_NULL );
152        assert( active_cor_p == 0 );
153        main_cor.m_pkg_p = this;
154		DEBUGF << &main_cor << ": is main co-routine" << std::endl;
155        active_cor_p = &main_cor;
156    }
157}
158
159
160// destructor
161
162sc_cor_pkg_pthread::~sc_cor_pkg_pthread()
163{
164    if( -- instance_count == 0 ) {
165        // cleanup the main coroutine
166    }
167}
168
169
170// create a new coroutine
171
172sc_cor*
173sc_cor_pkg_pthread::create( std::size_t stack_size, sc_cor_fn* fn, void* arg )
174{
175    sc_cor_pthread* cor_p = new sc_cor_pthread;
176    DEBUGF << &main_cor << ": sc_cor_pkg_pthread::create("
177	       << cor_p << ")" << std::endl;
178
179
180    // INITIALIZE OBJECT'S FIELDS FROM ARGUMENT LIST:
181
182    cor_p->m_pkg_p = this;
183    cor_p->m_cor_fn = fn;
184    cor_p->m_cor_fn_arg = arg;
185
186
187	// SET UP THREAD CREATION ATTRIBUTES:
188	//
189	// Use default values except for stack size. If stack size is non-zero
190	// set it.
191
192    pthread_attr_t attr;
193	pthread_attr_init( &attr );
194	if ( stack_size != 0 )
195	{
196		pthread_attr_setstacksize( &attr, stack_size );
197	}
198
199
200    // ALLOCATE THE POSIX THREAD TO USE AND FORCE SEQUENTIAL EXECUTION:
201    //
202    // Because pthread_create causes the created thread to be executed,
203    // we need to let it run until we can block in the invoke_module_method.
204    // So we:
205    //   (1) Lock the creation mutex before creating the new thread.
206    //   (2) Sleep on the creation condition, which will be signalled by
207    //       the newly created thread just before it goes to sleep in
208    //       invoke_module_method.
209    // This scheme results in the newly created thread being dormant before
210    // the main thread continues execution.
211
212    pthread_mutex_lock( &create_mutex );
213    DEBUGF << &main_cor << ": about to create actual thread "
214	       << cor_p << std::endl;
215    if ( pthread_create( &cor_p->m_thread, &attr,
216             &sc_cor_pthread::invoke_module_method, (void*)cor_p ) )
217    {
218        std::fprintf(stderr, "ERROR - could not create thread\n");
219    }
220
221    DEBUGF << &main_cor << ": main thread waiting for signal from "
222	       << cor_p << std::endl;
223    pthread_cond_wait( &create_condition, &create_mutex );
224	DEBUGF << &main_cor << ": main thread signaled by "
225	       << cor_p << endl;
226	pthread_attr_destroy( &attr );
227    pthread_mutex_unlock( &create_mutex );
228    DEBUGF << &main_cor << ": exiting sc_cor_pkg_pthread::create("
229	       << cor_p << ")" << std::endl;
230
231    return cor_p;
232}
233
234
235// yield to the next coroutine
236//
237// We don't do anything after the p_thread_cond_wait since it won't
238// happen until the thread wakes up again!
239
240void
241sc_cor_pkg_pthread::yield( sc_cor* next_cor_p )
242{
243    sc_cor_pthread* from_p = active_cor_p;
244    sc_cor_pthread* to_p = (sc_cor_pthread*)next_cor_p;
245
246    DEBUGF << from_p << ": switch to " << to_p << std::endl;
247    if ( to_p != from_p )
248    {
249        pthread_mutex_lock( &to_p->m_mutex );
250        pthread_cond_signal( &to_p->m_pt_condition );
251        pthread_mutex_lock( &from_p->m_mutex );
252        pthread_mutex_unlock( &to_p->m_mutex );
253        pthread_cond_wait( &from_p->m_pt_condition, &from_p->m_mutex );
254        pthread_mutex_unlock( &from_p->m_mutex );
255    }
256
257    active_cor_p = from_p; // When we come out of wait make ourselves active.
258	DEBUGF << from_p << " restarting after yield to " << to_p << std::endl;
259}
260
261
262// abort the current coroutine (and resume the next coroutine)
263
264void
265sc_cor_pkg_pthread::abort( sc_cor* next_cor_p )
266{
267    sc_cor_pthread* n_p = (sc_cor_pthread*)next_cor_p;
268
269    DEBUGF << active_cor_p << ": aborting, switching to " << n_p << std::endl;
270    pthread_mutex_lock( &n_p->m_mutex );
271    pthread_cond_signal( &n_p->m_pt_condition );
272    pthread_mutex_unlock( &n_p->m_mutex );
273}
274
275
276// get the main coroutine
277
278sc_cor*
279sc_cor_pkg_pthread::get_main()
280{
281    return &main_cor;
282}
283
284} // namespace sc_core
285
286#endif // !defined(_WIN32) && !defined(WIN32) && defined(SC_USE_PTHREADS)
287
288
289// $Log: sc_cor_pthread.cpp,v $
290// Revision 1.6  2011/08/30 21:51:04  acg
291//  Jerome Cornet: auto processing of pthread configurations.
292//
293// Revision 1.5  2011/08/26 20:46:09  acg
294//  Andy Goodrich: moved the modification log to the end of the file to
295//  eliminate source line number skew when check-ins are done.
296//
297// Revision 1.4  2011/02/18 20:27:14  acg
298//  Andy Goodrich: Updated Copyrights.
299//
300// Revision 1.3  2011/02/13 21:47:37  acg
301//  Andy Goodrich: update copyright notice.
302//
303// Revision 1.2  2008/05/22 17:06:25  acg
304//  Andy Goodrich: updated copyright notice to include 2008.
305//
306// Revision 1.1.1.1  2006/12/15 20:20:05  acg
307// SystemC 2.3
308//
309// Revision 1.3  2006/01/13 18:44:29  acg
310// Added $Log to record CVS changes into the source.
311//
312
313// Taf!
314