112027Sjungma@eit.uni-kl.de/*****************************************************************************
212027Sjungma@eit.uni-kl.de
312027Sjungma@eit.uni-kl.de  Licensed to Accellera Systems Initiative Inc. (Accellera) under one or
412027Sjungma@eit.uni-kl.de  more contributor license agreements.  See the NOTICE file distributed
512027Sjungma@eit.uni-kl.de  with this work for additional information regarding copyright ownership.
612027Sjungma@eit.uni-kl.de  Accellera licenses this file to you under the Apache License, Version 2.0
712027Sjungma@eit.uni-kl.de  (the "License"); you may not use this file except in compliance with the
812027Sjungma@eit.uni-kl.de  License.  You may obtain a copy of the License at
912027Sjungma@eit.uni-kl.de
1012027Sjungma@eit.uni-kl.de    http://www.apache.org/licenses/LICENSE-2.0
1112027Sjungma@eit.uni-kl.de
1212027Sjungma@eit.uni-kl.de  Unless required by applicable law or agreed to in writing, software
1312027Sjungma@eit.uni-kl.de  distributed under the License is distributed on an "AS IS" BASIS,
1412027Sjungma@eit.uni-kl.de  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
1512027Sjungma@eit.uni-kl.de  implied.  See the License for the specific language governing
1612027Sjungma@eit.uni-kl.de  permissions and limitations under the License.
1712027Sjungma@eit.uni-kl.de
1812027Sjungma@eit.uni-kl.de *****************************************************************************/
1912027Sjungma@eit.uni-kl.de
2012027Sjungma@eit.uni-kl.de/*****************************************************************************
2112027Sjungma@eit.uni-kl.de
2212027Sjungma@eit.uni-kl.de  sc_cor_pthread.cpp -- Coroutine implementation with pthreads.
2312027Sjungma@eit.uni-kl.de
2412027Sjungma@eit.uni-kl.de  Original Author: Andy Goodrich, Forte Design Systems, 2002-11-10
2512027Sjungma@eit.uni-kl.de
2612027Sjungma@eit.uni-kl.de CHANGE LOG APPEARS AT THE END OF THE FILE
2712027Sjungma@eit.uni-kl.de *****************************************************************************/
2812027Sjungma@eit.uni-kl.de
2912027Sjungma@eit.uni-kl.de#if !defined(_WIN32) && !defined(WIN32) && defined(SC_USE_PTHREADS)
3012027Sjungma@eit.uni-kl.de
3112027Sjungma@eit.uni-kl.de// ORDER OF THE INCLUDES AND namespace sc_core IS IMPORTANT!!!
3212027Sjungma@eit.uni-kl.de
3312027Sjungma@eit.uni-kl.de#include "sysc/kernel/sc_cor_pthread.h"
3412027Sjungma@eit.uni-kl.de#include "sysc/kernel/sc_simcontext.h"
3512027Sjungma@eit.uni-kl.de
3612027Sjungma@eit.uni-kl.deusing namespace std;
3712027Sjungma@eit.uni-kl.de
3812027Sjungma@eit.uni-kl.denamespace sc_core {
3912027Sjungma@eit.uni-kl.de
4012027Sjungma@eit.uni-kl.de// MAKE SURE WE HAVE A NULL THAT WILL WORK:
4112027Sjungma@eit.uni-kl.de
4212027Sjungma@eit.uni-kl.de#if defined(__hpux)
4312027Sjungma@eit.uni-kl.de#   define PTHREAD_NULL cma_c_null
4412027Sjungma@eit.uni-kl.de#else  // !defined(__hpux)
4512027Sjungma@eit.uni-kl.de#   define PTHREAD_NULL NULL
4612027Sjungma@eit.uni-kl.de#endif // !defined(__hpux)
4712027Sjungma@eit.uni-kl.de
4812027Sjungma@eit.uni-kl.de#define DEBUGF \
4912027Sjungma@eit.uni-kl.de    if (0) std::cout << "sc_cor_pthread.cpp(" << __LINE__ << ") "
5012027Sjungma@eit.uni-kl.de
5112027Sjungma@eit.uni-kl.de// ----------------------------------------------------------------------------
5212027Sjungma@eit.uni-kl.de//  File static variables.
5312027Sjungma@eit.uni-kl.de//
5412027Sjungma@eit.uni-kl.de// (1) The thread creation mutex and the creation condition are used to
5512027Sjungma@eit.uni-kl.de//     suspend the thread creating another one until the created thread
5612027Sjungma@eit.uni-kl.de//     reaches its invoke_module_method. This allows us to get control of
5712027Sjungma@eit.uni-kl.de//     thread scheduling away from the pthread package.
5812027Sjungma@eit.uni-kl.de// ----------------------------------------------------------------------------
5912027Sjungma@eit.uni-kl.de
6012027Sjungma@eit.uni-kl.destatic sc_cor_pthread* active_cor_p=0;   // Active co-routine.
6112027Sjungma@eit.uni-kl.destatic pthread_cond_t  create_condition; // See note 1 above.
6212027Sjungma@eit.uni-kl.destatic pthread_mutex_t create_mutex;     // See note 1 above.
6312027Sjungma@eit.uni-kl.destatic sc_cor_pthread  main_cor;         // Main coroutine.
6412027Sjungma@eit.uni-kl.de
6512027Sjungma@eit.uni-kl.de
6612027Sjungma@eit.uni-kl.de// ----------------------------------------------------------------------------
6712027Sjungma@eit.uni-kl.de//  CLASS : sc_cor_pthread
6812027Sjungma@eit.uni-kl.de//
6912027Sjungma@eit.uni-kl.de//  Coroutine class implemented with Posix Threads.
7012027Sjungma@eit.uni-kl.de// ----------------------------------------------------------------------------
7112027Sjungma@eit.uni-kl.de
7212027Sjungma@eit.uni-kl.de// constructor
7312027Sjungma@eit.uni-kl.de
7412027Sjungma@eit.uni-kl.desc_cor_pthread::sc_cor_pthread()
7512027Sjungma@eit.uni-kl.de    : m_cor_fn_arg( 0 ), m_pkg_p( 0 )
7612027Sjungma@eit.uni-kl.de{
7712027Sjungma@eit.uni-kl.de    DEBUGF << this << ": sc_cor_pthread::sc_cor_pthread()" << std::endl;
7812027Sjungma@eit.uni-kl.de    pthread_cond_init( &m_pt_condition, PTHREAD_NULL );
7912027Sjungma@eit.uni-kl.de    pthread_mutex_init( &m_mutex, PTHREAD_NULL );
8012027Sjungma@eit.uni-kl.de}
8112027Sjungma@eit.uni-kl.de
8212027Sjungma@eit.uni-kl.de
8312027Sjungma@eit.uni-kl.de// destructor
8412027Sjungma@eit.uni-kl.de
8512027Sjungma@eit.uni-kl.desc_cor_pthread::~sc_cor_pthread()
8612027Sjungma@eit.uni-kl.de{
8712027Sjungma@eit.uni-kl.de    DEBUGF << this << ": sc_cor_pthread::~sc_cor_pthread()" << std::endl;
8812027Sjungma@eit.uni-kl.de	pthread_cond_destroy( &m_pt_condition);
8912027Sjungma@eit.uni-kl.de	pthread_mutex_destroy( &m_mutex );
9012027Sjungma@eit.uni-kl.de}
9112027Sjungma@eit.uni-kl.de
9212027Sjungma@eit.uni-kl.de
9312027Sjungma@eit.uni-kl.de// This static method is a Posix Threads helper callback and invokes a thread
9412027Sjungma@eit.uni-kl.de// for the first time. It performs some synchronization and then invokes the
9512027Sjungma@eit.uni-kl.de// actual sc_cor helper function.
9612027Sjungma@eit.uni-kl.de//     context_p -> thread to invoke module method of.
9712027Sjungma@eit.uni-kl.de// Result is 0 and ignored.
9812027Sjungma@eit.uni-kl.de
9912027Sjungma@eit.uni-kl.devoid* sc_cor_pthread::invoke_module_method(void* context_p)
10012027Sjungma@eit.uni-kl.de{
10112027Sjungma@eit.uni-kl.de    sc_cor_pthread* p = (sc_cor_pthread*)context_p;
10212027Sjungma@eit.uni-kl.de    DEBUGF << p << ": sc_cor_pthread::invoke_module_method()" << std::endl;
10312027Sjungma@eit.uni-kl.de
10412027Sjungma@eit.uni-kl.de
10512027Sjungma@eit.uni-kl.de    // SUSPEND THE THREAD SO WE CAN GAIN CONTROL FROM THE PTHREAD PACKAGE:
10612027Sjungma@eit.uni-kl.de    //
10712027Sjungma@eit.uni-kl.de    // Since pthread_create schedules each thread behind our back for its
10812027Sjungma@eit.uni-kl.de    // initial execution we immediately suspend a newly created thread
10912027Sjungma@eit.uni-kl.de    // here so we can control when its execution will occur. We also wake
11012027Sjungma@eit.uni-kl.de    // up the main thread which is waiting for this thread to execute to this
11112027Sjungma@eit.uni-kl.de    // wait point.
11212027Sjungma@eit.uni-kl.de
11312027Sjungma@eit.uni-kl.de    pthread_mutex_lock( &create_mutex );
11412027Sjungma@eit.uni-kl.de	DEBUGF << p << ": child signalling main thread " << endl;
11512027Sjungma@eit.uni-kl.de    pthread_cond_signal( &create_condition );
11612027Sjungma@eit.uni-kl.de    pthread_mutex_lock( &p->m_mutex );
11712027Sjungma@eit.uni-kl.de    pthread_mutex_unlock( &create_mutex );
11812027Sjungma@eit.uni-kl.de    pthread_cond_wait( &p->m_pt_condition, &p->m_mutex );
11912027Sjungma@eit.uni-kl.de    pthread_mutex_unlock( &p->m_mutex );
12012027Sjungma@eit.uni-kl.de
12112027Sjungma@eit.uni-kl.de
12212027Sjungma@eit.uni-kl.de    // CALL THE SYSTEMC CODE THAT WILL ACTUALLY START THE THREAD OFF:
12312027Sjungma@eit.uni-kl.de
12412027Sjungma@eit.uni-kl.de    active_cor_p = p;
12512027Sjungma@eit.uni-kl.de    DEBUGF << p << ": about to invoke real method "
12612027Sjungma@eit.uni-kl.de	   << active_cor_p << std::endl;
12712027Sjungma@eit.uni-kl.de    (p->m_cor_fn)(p->m_cor_fn_arg);
12812027Sjungma@eit.uni-kl.de
12912027Sjungma@eit.uni-kl.de    return 0;
13012027Sjungma@eit.uni-kl.de}
13112027Sjungma@eit.uni-kl.de
13212027Sjungma@eit.uni-kl.de
13312027Sjungma@eit.uni-kl.de// ----------------------------------------------------------------------------
13412027Sjungma@eit.uni-kl.de//  CLASS : sc_cor_pkg_pthread
13512027Sjungma@eit.uni-kl.de//
13612027Sjungma@eit.uni-kl.de//  Coroutine package class implemented with Posix Threads.
13712027Sjungma@eit.uni-kl.de// ----------------------------------------------------------------------------
13812027Sjungma@eit.uni-kl.de
13912027Sjungma@eit.uni-kl.deint sc_cor_pkg_pthread::instance_count = 0;
14012027Sjungma@eit.uni-kl.de
14112027Sjungma@eit.uni-kl.de
14212027Sjungma@eit.uni-kl.de// constructor
14312027Sjungma@eit.uni-kl.de
14412027Sjungma@eit.uni-kl.desc_cor_pkg_pthread::sc_cor_pkg_pthread( sc_simcontext* simc )
14512027Sjungma@eit.uni-kl.de: sc_cor_pkg( simc )
14612027Sjungma@eit.uni-kl.de{
14712027Sjungma@eit.uni-kl.de    // initialize the current coroutine
14812027Sjungma@eit.uni-kl.de    if( ++ instance_count == 1 )
14912027Sjungma@eit.uni-kl.de    {
15012027Sjungma@eit.uni-kl.de        pthread_cond_init( &create_condition, PTHREAD_NULL );
15112027Sjungma@eit.uni-kl.de        pthread_mutex_init( &create_mutex, PTHREAD_NULL );
15212027Sjungma@eit.uni-kl.de        assert( active_cor_p == 0 );
15312027Sjungma@eit.uni-kl.de        main_cor.m_pkg_p = this;
15412027Sjungma@eit.uni-kl.de		DEBUGF << &main_cor << ": is main co-routine" << std::endl;
15512027Sjungma@eit.uni-kl.de        active_cor_p = &main_cor;
15612027Sjungma@eit.uni-kl.de    }
15712027Sjungma@eit.uni-kl.de}
15812027Sjungma@eit.uni-kl.de
15912027Sjungma@eit.uni-kl.de
16012027Sjungma@eit.uni-kl.de// destructor
16112027Sjungma@eit.uni-kl.de
16212027Sjungma@eit.uni-kl.desc_cor_pkg_pthread::~sc_cor_pkg_pthread()
16312027Sjungma@eit.uni-kl.de{
16412027Sjungma@eit.uni-kl.de    if( -- instance_count == 0 ) {
16512027Sjungma@eit.uni-kl.de        // cleanup the main coroutine
16612027Sjungma@eit.uni-kl.de    }
16712027Sjungma@eit.uni-kl.de}
16812027Sjungma@eit.uni-kl.de
16912027Sjungma@eit.uni-kl.de
17012027Sjungma@eit.uni-kl.de// create a new coroutine
17112027Sjungma@eit.uni-kl.de
17212027Sjungma@eit.uni-kl.desc_cor*
17312027Sjungma@eit.uni-kl.desc_cor_pkg_pthread::create( std::size_t stack_size, sc_cor_fn* fn, void* arg )
17412027Sjungma@eit.uni-kl.de{
17512027Sjungma@eit.uni-kl.de    sc_cor_pthread* cor_p = new sc_cor_pthread;
17612027Sjungma@eit.uni-kl.de    DEBUGF << &main_cor << ": sc_cor_pkg_pthread::create("
17712027Sjungma@eit.uni-kl.de	       << cor_p << ")" << std::endl;
17812027Sjungma@eit.uni-kl.de
17912027Sjungma@eit.uni-kl.de
18012027Sjungma@eit.uni-kl.de    // INITIALIZE OBJECT'S FIELDS FROM ARGUMENT LIST:
18112027Sjungma@eit.uni-kl.de
18212027Sjungma@eit.uni-kl.de    cor_p->m_pkg_p = this;
18312027Sjungma@eit.uni-kl.de    cor_p->m_cor_fn = fn;
18412027Sjungma@eit.uni-kl.de    cor_p->m_cor_fn_arg = arg;
18512027Sjungma@eit.uni-kl.de
18612027Sjungma@eit.uni-kl.de
18712027Sjungma@eit.uni-kl.de	// SET UP THREAD CREATION ATTRIBUTES:
18812027Sjungma@eit.uni-kl.de	//
18912027Sjungma@eit.uni-kl.de	// Use default values except for stack size. If stack size is non-zero
19012027Sjungma@eit.uni-kl.de	// set it.
19112027Sjungma@eit.uni-kl.de
19212027Sjungma@eit.uni-kl.de    pthread_attr_t attr;
19312027Sjungma@eit.uni-kl.de	pthread_attr_init( &attr );
19412027Sjungma@eit.uni-kl.de	if ( stack_size != 0 )
19512027Sjungma@eit.uni-kl.de	{
19612027Sjungma@eit.uni-kl.de		pthread_attr_setstacksize( &attr, stack_size );
19712027Sjungma@eit.uni-kl.de	}
19812027Sjungma@eit.uni-kl.de
19912027Sjungma@eit.uni-kl.de
20012027Sjungma@eit.uni-kl.de    // ALLOCATE THE POSIX THREAD TO USE AND FORCE SEQUENTIAL EXECUTION:
20112027Sjungma@eit.uni-kl.de    //
20212027Sjungma@eit.uni-kl.de    // Because pthread_create causes the created thread to be executed,
20312027Sjungma@eit.uni-kl.de    // we need to let it run until we can block in the invoke_module_method.
20412027Sjungma@eit.uni-kl.de    // So we:
20512027Sjungma@eit.uni-kl.de    //   (1) Lock the creation mutex before creating the new thread.
20612027Sjungma@eit.uni-kl.de    //   (2) Sleep on the creation condition, which will be signalled by
20712027Sjungma@eit.uni-kl.de    //       the newly created thread just before it goes to sleep in
20812027Sjungma@eit.uni-kl.de    //       invoke_module_method.
20912027Sjungma@eit.uni-kl.de    // This scheme results in the newly created thread being dormant before
21012027Sjungma@eit.uni-kl.de    // the main thread continues execution.
21112027Sjungma@eit.uni-kl.de
21212027Sjungma@eit.uni-kl.de    pthread_mutex_lock( &create_mutex );
21312027Sjungma@eit.uni-kl.de    DEBUGF << &main_cor << ": about to create actual thread "
21412027Sjungma@eit.uni-kl.de	       << cor_p << std::endl;
21512027Sjungma@eit.uni-kl.de    if ( pthread_create( &cor_p->m_thread, &attr,
21612027Sjungma@eit.uni-kl.de             &sc_cor_pthread::invoke_module_method, (void*)cor_p ) )
21712027Sjungma@eit.uni-kl.de    {
21812027Sjungma@eit.uni-kl.de        std::fprintf(stderr, "ERROR - could not create thread\n");
21912027Sjungma@eit.uni-kl.de    }
22012027Sjungma@eit.uni-kl.de
22112027Sjungma@eit.uni-kl.de    DEBUGF << &main_cor << ": main thread waiting for signal from "
22212027Sjungma@eit.uni-kl.de	       << cor_p << std::endl;
22312027Sjungma@eit.uni-kl.de    pthread_cond_wait( &create_condition, &create_mutex );
22412027Sjungma@eit.uni-kl.de	DEBUGF << &main_cor << ": main thread signaled by "
22512027Sjungma@eit.uni-kl.de	       << cor_p << endl;
22612027Sjungma@eit.uni-kl.de	pthread_attr_destroy( &attr );
22712027Sjungma@eit.uni-kl.de    pthread_mutex_unlock( &create_mutex );
22812027Sjungma@eit.uni-kl.de    DEBUGF << &main_cor << ": exiting sc_cor_pkg_pthread::create("
22912027Sjungma@eit.uni-kl.de	       << cor_p << ")" << std::endl;
23012027Sjungma@eit.uni-kl.de
23112027Sjungma@eit.uni-kl.de    return cor_p;
23212027Sjungma@eit.uni-kl.de}
23312027Sjungma@eit.uni-kl.de
23412027Sjungma@eit.uni-kl.de
23512027Sjungma@eit.uni-kl.de// yield to the next coroutine
23612027Sjungma@eit.uni-kl.de//
23712027Sjungma@eit.uni-kl.de// We don't do anything after the p_thread_cond_wait since it won't
23812027Sjungma@eit.uni-kl.de// happen until the thread wakes up again!
23912027Sjungma@eit.uni-kl.de
24012027Sjungma@eit.uni-kl.devoid
24112027Sjungma@eit.uni-kl.desc_cor_pkg_pthread::yield( sc_cor* next_cor_p )
24212027Sjungma@eit.uni-kl.de{
24312027Sjungma@eit.uni-kl.de    sc_cor_pthread* from_p = active_cor_p;
24412027Sjungma@eit.uni-kl.de    sc_cor_pthread* to_p = (sc_cor_pthread*)next_cor_p;
24512027Sjungma@eit.uni-kl.de
24612027Sjungma@eit.uni-kl.de    DEBUGF << from_p << ": switch to " << to_p << std::endl;
24712027Sjungma@eit.uni-kl.de    if ( to_p != from_p )
24812027Sjungma@eit.uni-kl.de    {
24912027Sjungma@eit.uni-kl.de        pthread_mutex_lock( &to_p->m_mutex );
25012027Sjungma@eit.uni-kl.de        pthread_cond_signal( &to_p->m_pt_condition );
25112027Sjungma@eit.uni-kl.de        pthread_mutex_lock( &from_p->m_mutex );
25212027Sjungma@eit.uni-kl.de        pthread_mutex_unlock( &to_p->m_mutex );
25312027Sjungma@eit.uni-kl.de        pthread_cond_wait( &from_p->m_pt_condition, &from_p->m_mutex );
25412027Sjungma@eit.uni-kl.de        pthread_mutex_unlock( &from_p->m_mutex );
25512027Sjungma@eit.uni-kl.de    }
25612027Sjungma@eit.uni-kl.de
25712027Sjungma@eit.uni-kl.de    active_cor_p = from_p; // When we come out of wait make ourselves active.
25812027Sjungma@eit.uni-kl.de	DEBUGF << from_p << " restarting after yield to " << to_p << std::endl;
25912027Sjungma@eit.uni-kl.de}
26012027Sjungma@eit.uni-kl.de
26112027Sjungma@eit.uni-kl.de
26212027Sjungma@eit.uni-kl.de// abort the current coroutine (and resume the next coroutine)
26312027Sjungma@eit.uni-kl.de
26412027Sjungma@eit.uni-kl.devoid
26512027Sjungma@eit.uni-kl.desc_cor_pkg_pthread::abort( sc_cor* next_cor_p )
26612027Sjungma@eit.uni-kl.de{
26712027Sjungma@eit.uni-kl.de    sc_cor_pthread* n_p = (sc_cor_pthread*)next_cor_p;
26812027Sjungma@eit.uni-kl.de
26912027Sjungma@eit.uni-kl.de    DEBUGF << active_cor_p << ": aborting, switching to " << n_p << std::endl;
27012027Sjungma@eit.uni-kl.de    pthread_mutex_lock( &n_p->m_mutex );
27112027Sjungma@eit.uni-kl.de    pthread_cond_signal( &n_p->m_pt_condition );
27212027Sjungma@eit.uni-kl.de    pthread_mutex_unlock( &n_p->m_mutex );
27312027Sjungma@eit.uni-kl.de}
27412027Sjungma@eit.uni-kl.de
27512027Sjungma@eit.uni-kl.de
27612027Sjungma@eit.uni-kl.de// get the main coroutine
27712027Sjungma@eit.uni-kl.de
27812027Sjungma@eit.uni-kl.desc_cor*
27912027Sjungma@eit.uni-kl.desc_cor_pkg_pthread::get_main()
28012027Sjungma@eit.uni-kl.de{
28112027Sjungma@eit.uni-kl.de    return &main_cor;
28212027Sjungma@eit.uni-kl.de}
28312027Sjungma@eit.uni-kl.de
28412027Sjungma@eit.uni-kl.de} // namespace sc_core
28512027Sjungma@eit.uni-kl.de
28612027Sjungma@eit.uni-kl.de#endif // !defined(_WIN32) && !defined(WIN32) && defined(SC_USE_PTHREADS)
28712027Sjungma@eit.uni-kl.de
28812027Sjungma@eit.uni-kl.de
28912027Sjungma@eit.uni-kl.de// $Log: sc_cor_pthread.cpp,v $
29012027Sjungma@eit.uni-kl.de// Revision 1.6  2011/08/30 21:51:04  acg
29112027Sjungma@eit.uni-kl.de//  Jerome Cornet: auto processing of pthread configurations.
29212027Sjungma@eit.uni-kl.de//
29312027Sjungma@eit.uni-kl.de// Revision 1.5  2011/08/26 20:46:09  acg
29412027Sjungma@eit.uni-kl.de//  Andy Goodrich: moved the modification log to the end of the file to
29512027Sjungma@eit.uni-kl.de//  eliminate source line number skew when check-ins are done.
29612027Sjungma@eit.uni-kl.de//
29712027Sjungma@eit.uni-kl.de// Revision 1.4  2011/02/18 20:27:14  acg
29812027Sjungma@eit.uni-kl.de//  Andy Goodrich: Updated Copyrights.
29912027Sjungma@eit.uni-kl.de//
30012027Sjungma@eit.uni-kl.de// Revision 1.3  2011/02/13 21:47:37  acg
30112027Sjungma@eit.uni-kl.de//  Andy Goodrich: update copyright notice.
30212027Sjungma@eit.uni-kl.de//
30312027Sjungma@eit.uni-kl.de// Revision 1.2  2008/05/22 17:06:25  acg
30412027Sjungma@eit.uni-kl.de//  Andy Goodrich: updated copyright notice to include 2008.
30512027Sjungma@eit.uni-kl.de//
30612027Sjungma@eit.uni-kl.de// Revision 1.1.1.1  2006/12/15 20:20:05  acg
30712027Sjungma@eit.uni-kl.de// SystemC 2.3
30812027Sjungma@eit.uni-kl.de//
30912027Sjungma@eit.uni-kl.de// Revision 1.3  2006/01/13 18:44:29  acg
31012027Sjungma@eit.uni-kl.de// Added $Log to record CVS changes into the source.
31112027Sjungma@eit.uni-kl.de//
31212027Sjungma@eit.uni-kl.de
31312027Sjungma@eit.uni-kl.de// Taf!
314