]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/mac/carbon/thread.cpp
replaced wxNO_FULL_REPAINT_ON_RESIZE with wxFULL_REPAINT_ON_RESIZE in XTI declarations
[wxWidgets.git] / src / mac / carbon / thread.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: thread.cpp
3// Purpose: wxThread Implementation
4// Author: Original from Wolfram Gloger/Guilhem Lavaux/Vadim Zeitlin
5// Modified by: Stefan Csomor
6// Created: 04/22/98
7// RCS-ID: $Id$
8// Copyright: (c) Wolfram Gloger (1996, 1997); Guilhem Lavaux (1998),
9// Vadim Zeitlin (1999) , Stefan Csomor (2000)
10// Licence: wxWindows licence
11/////////////////////////////////////////////////////////////////////////////
12
13#ifdef __GNUG__
14 #pragma implementation "thread.h"
15#endif
16
17// ----------------------------------------------------------------------------
18// headers
19// ----------------------------------------------------------------------------
20
21// For compilers that support precompilation, includes "wx.h".
22#include "wx/wxprec.h"
23
24#if defined(__BORLANDC__)
25 #pragma hdrstop
26#endif
27
28#ifndef WX_PRECOMP
29 #include "wx/wx.h"
30#endif
31
32#if wxUSE_THREADS
33
34#include "wx/module.h"
35#include "wx/thread.h"
36
37#ifdef __WXMAC__
38#include <Threads.h>
39#include "wx/mac/uma.h"
40#include "wx/mac/macnotfy.h"
41#endif
42
43#define INFINITE 0xFFFFFFFF
44
45
46// ----------------------------------------------------------------------------
47// constants
48// ----------------------------------------------------------------------------
49
50// the possible states of the thread ("=>" shows all possible transitions from
51// this state)
52enum wxThreadState
53{
54 STATE_NEW, // didn't start execution yet (=> RUNNING)
55 STATE_RUNNING, // thread is running (=> PAUSED, CANCELED)
56 STATE_PAUSED, // thread is temporarily suspended (=> RUNNING)
57 STATE_CANCELED, // thread should terminate a.s.a.p. (=> EXITED)
58 STATE_EXITED // thread is terminating
59};
60
61// ----------------------------------------------------------------------------
62// this module globals
63// ----------------------------------------------------------------------------
64
65static ThreadID gs_idMainThread = kNoThreadID ;
66static bool gs_waitingForThread = FALSE ;
67
68// ============================================================================
69// MacOS implementation of thread classes
70// ============================================================================
71
72class wxMacStCritical
73{
74public :
75 wxMacStCritical()
76 {
77 if ( UMASystemIsInitialized() )
78 ThreadBeginCritical() ;
79 }
80 ~wxMacStCritical()
81 {
82 if ( UMASystemIsInitialized() )
83 ThreadEndCritical() ;
84 }
85};
86
87// ----------------------------------------------------------------------------
88// wxMutex implementation
89// ----------------------------------------------------------------------------
90
91class wxMutexInternal
92{
93public:
94 wxMutexInternal(wxMutexType WXUNUSED(mutexType))
95 {
96 m_owner = kNoThreadID ;
97 m_locked = 0;
98 }
99
100 ~wxMutexInternal()
101 {
102 if ( m_locked > 0 )
103 {
104 wxLogDebug(_T("Warning: freeing a locked mutex (%ld locks)."), m_locked);
105 }
106 }
107
108 bool IsOk() const { return true; }
109
110 wxMutexError Lock() ;
111 wxMutexError TryLock() ;
112 wxMutexError Unlock();
113public:
114 ThreadID m_owner ;
115 wxArrayLong m_waiters ;
116 long m_locked ;
117};
118
119wxMutexError wxMutexInternal::Lock()
120{
121 wxMacStCritical critical ;
122 if ( UMASystemIsInitialized() )
123 {
124 OSErr err ;
125 ThreadID current = kNoThreadID;
126 err = ::MacGetCurrentThread(&current);
127 // if we are not the owner, add this thread to the list of waiting threads, stop this thread
128 // and invoke the scheduler to continue executing the owner's thread
129 while ( m_owner != kNoThreadID && m_owner != current)
130 {
131 m_waiters.Add(current);
132 err = ::SetThreadStateEndCritical(kCurrentThreadID, kStoppedThreadState, m_owner);
133 err = ::ThreadBeginCritical();
134 }
135 m_owner = current;
136 }
137 m_locked++;
138
139 return wxMUTEX_NO_ERROR;
140}
141
142wxMutexError wxMutexInternal::TryLock()
143{
144 wxMacStCritical critical ;
145 if ( UMASystemIsInitialized() )
146 {
147 ThreadID current = kNoThreadID;
148 ::MacGetCurrentThread(&current);
149 // if we are not the owner, give an error back
150 if ( m_owner != kNoThreadID && m_owner != current )
151 return wxMUTEX_BUSY;
152
153 m_owner = current;
154 }
155 m_locked++;
156
157 return wxMUTEX_NO_ERROR;
158}
159
160wxMutexError wxMutexInternal::Unlock()
161{
162 if ( UMASystemIsInitialized() )
163 {
164 OSErr err;
165 err = ::ThreadBeginCritical();
166
167 if (m_locked > 0)
168 m_locked--;
169
170 // this mutex is not owned by anybody anmore
171 m_owner = kNoThreadID;
172
173 // now pass on to the first waiting thread
174 ThreadID firstWaiting = kNoThreadID;
175 bool found = false;
176 while (!m_waiters.IsEmpty() && !found)
177 {
178 firstWaiting = m_waiters[0];
179 err = ::SetThreadState(firstWaiting, kReadyThreadState, kNoThreadID);
180 // in case this was not successful (dead thread), we just loop on and reset the id
181 found = (err != threadNotFoundErr);
182 if ( !found )
183 firstWaiting = kNoThreadID ;
184 m_waiters.RemoveAt(0) ;
185 }
186 // now we have a valid firstWaiting thread, which has been scheduled to run next, just end the
187 // critical section and invoke the scheduler
188 err = ::SetThreadStateEndCritical(kCurrentThreadID, kReadyThreadState, firstWaiting);
189 }
190 else
191 {
192 if (m_locked > 0)
193 m_locked--;
194 }
195 return wxMUTEX_NO_ERROR;
196}
197
198// --------------------------------------------------------------------------
199// wxSemaphore
200// --------------------------------------------------------------------------
201
202// TODO not yet implemented
203
204class wxSemaphoreInternal
205{
206public:
207 wxSemaphoreInternal(int initialcount, int maxcount);
208 ~wxSemaphoreInternal();
209
210 bool IsOk() const { return true ; }
211
212 wxSemaError Wait() { return WaitTimeout(INFINITE); }
213 wxSemaError TryWait() { return WaitTimeout(0); }
214 wxSemaError WaitTimeout(unsigned long milliseconds);
215
216 wxSemaError Post();
217
218private:
219};
220
221wxSemaphoreInternal::wxSemaphoreInternal(int initialcount, int maxcount)
222{
223 if ( maxcount == 0 )
224 {
225 // make it practically infinite
226 maxcount = INT_MAX;
227 }
228}
229
230wxSemaphoreInternal::~wxSemaphoreInternal()
231{
232}
233
234wxSemaError wxSemaphoreInternal::WaitTimeout(unsigned long milliseconds)
235{
236 return wxSEMA_MISC_ERROR;
237}
238
239wxSemaError wxSemaphoreInternal::Post()
240{
241 return wxSEMA_MISC_ERROR;
242}
243
244// ----------------------------------------------------------------------------
245// wxCondition implementation
246// ----------------------------------------------------------------------------
247
248// TODO this is not yet completed
249
250class wxConditionInternal
251{
252public:
253 wxConditionInternal(wxMutex& mutex) : m_mutex(mutex)
254 {
255 m_excessSignals = 0 ;
256 }
257 ~wxConditionInternal()
258 {
259 }
260
261 bool IsOk() const { return m_mutex.IsOk() ; }
262
263 wxCondError Wait()
264 {
265 return WaitTimeout(0xFFFFFFFF );
266 }
267
268 wxCondError WaitTimeout(unsigned long msectimeout)
269 {
270 wxMacStCritical critical ;
271 if ( m_excessSignals > 0 )
272 {
273 --m_excessSignals ;
274 return wxCOND_NO_ERROR ;
275 }
276 else if ( msectimeout == 0 )
277 {
278 return wxCOND_MISC_ERROR ;
279 }
280 else
281 {
282 }
283 /*
284 waiters++;
285
286 // FIXME this should be MsgWaitForMultipleObjects() as well probably
287 DWORD rc = ::WaitForSingleObject(event, timeout);
288
289 waiters--;
290
291 return rc != WAIT_TIMEOUT;
292 */
293 return wxCOND_NO_ERROR ;
294 }
295 wxCondError Signal()
296 {
297 wxMacStCritical critical ;
298 return wxCOND_NO_ERROR;
299 }
300
301 wxCondError Broadcast()
302 {
303 wxMacStCritical critical ;
304 return wxCOND_NO_ERROR;
305 }
306
307 wxArrayLong m_waiters ;
308 wxInt32 m_excessSignals ;
309 wxMutex& m_mutex;
310};
311
312// ----------------------------------------------------------------------------
313// wxCriticalSection implementation
314// ----------------------------------------------------------------------------
315
316// it's implemented as a mutex on mac os, so it is defined in the headers
317
318// ----------------------------------------------------------------------------
319// wxThread implementation
320// ----------------------------------------------------------------------------
321
322// wxThreadInternal class
323// ----------------------
324
325class wxThreadInternal
326{
327public:
328 wxThreadInternal()
329 {
330 m_tid = kNoThreadID ;
331 m_state = STATE_NEW;
332 m_priority = WXTHREAD_DEFAULT_PRIORITY;
333 }
334
335 ~wxThreadInternal()
336 {
337 }
338
339 void Free()
340 {
341 }
342
343 // create a new (suspended) thread (for the given thread object)
344 bool Create(wxThread *thread, unsigned int stackSize);
345
346 // suspend/resume/terminate
347 bool Suspend();
348 bool Resume();
349 void Cancel() { m_state = STATE_CANCELED; }
350
351 // thread state
352 void SetState(wxThreadState state) { m_state = state; }
353 wxThreadState GetState() const { return m_state; }
354
355 // thread priority
356 void SetPriority(unsigned int priority);
357 unsigned int GetPriority() const { return m_priority; }
358
359 void SetResult( void *res ) { m_result = res ; }
360 void *GetResult() { return m_result ; }
361
362 // thread handle and id
363 ThreadID GetId() const { return m_tid; }
364
365 // thread function
366 static pascal void* MacThreadStart(wxThread* arg);
367
368private:
369 wxThreadState m_state; // state, see wxThreadState enum
370 unsigned int m_priority; // thread priority in "wx" units
371 ThreadID m_tid; // thread id
372 void* m_result;
373 static ThreadEntryUPP s_threadEntry ;
374};
375
376static wxArrayPtrVoid s_threads ;
377
378ThreadEntryUPP wxThreadInternal::s_threadEntry = NULL ;
379pascal void* wxThreadInternal::MacThreadStart(wxThread *thread)
380{
381 // first of all, check whether we hadn't been cancelled already
382 if ( thread->m_internal->GetState() == STATE_EXITED )
383 {
384 return (void*)-1;
385 }
386
387 void* rc = thread->Entry();
388
389 // enter m_critsect before changing the thread state
390 thread->m_critsect.Enter();
391 bool wasCancelled = thread->m_internal->GetState() == STATE_CANCELED;
392 thread->m_internal->SetState(STATE_EXITED);
393 thread->m_critsect.Leave();
394
395 thread->OnExit();
396
397 // if the thread was cancelled (from Delete()), then it the handle is still
398 // needed there
399 if ( thread->IsDetached() && !wasCancelled )
400 {
401 // auto delete
402 delete thread;
403 }
404 //else: the joinable threads handle will be closed when Wait() is done
405
406 return rc;
407}
408void wxThreadInternal::SetPriority(unsigned int priority)
409{
410 // Priorities don't exist on Mac
411}
412
413bool wxThreadInternal::Create(wxThread *thread, unsigned int stackSize)
414{
415 if ( s_threadEntry == NULL )
416 {
417 s_threadEntry = NewThreadEntryUPP( (ThreadEntryProcPtr) MacThreadStart ) ;
418 }
419 OSErr err = NewThread( kCooperativeThread,
420 s_threadEntry,
421 (void*) thread,
422 stackSize,
423 kNewSuspend,
424 &m_result,
425 &m_tid );
426
427 if ( err != noErr )
428 {
429 wxLogSysError(_("Can't create thread"));
430 return FALSE;
431 }
432
433 if ( m_priority != WXTHREAD_DEFAULT_PRIORITY )
434 {
435 SetPriority(m_priority);
436 }
437
438 m_state = STATE_NEW;
439
440 return TRUE;
441}
442
443bool wxThreadInternal::Suspend()
444{
445 OSErr err ;
446
447 ::ThreadBeginCritical();
448
449 if ( m_state != STATE_RUNNING )
450 {
451 ::ThreadEndCritical() ;
452 wxLogSysError(_("Can not suspend thread %x"), m_tid);
453 return FALSE;
454 }
455
456 m_state = STATE_PAUSED;
457
458 err = ::SetThreadStateEndCritical(m_tid, kStoppedThreadState, kNoThreadID);
459
460 return TRUE;
461}
462
463bool wxThreadInternal::Resume()
464{
465 ThreadID current ;
466 OSErr err ;
467 err = MacGetCurrentThread( &current ) ;
468
469 wxASSERT( err == noErr ) ;
470 wxASSERT( current != m_tid ) ;
471
472 ::ThreadBeginCritical();
473 if ( m_state != STATE_PAUSED && m_state != STATE_NEW )
474 {
475 ::ThreadEndCritical() ;
476 wxLogSysError(_("Can not resume thread %x"), m_tid);
477 return FALSE;
478
479 }
480 err = ::SetThreadStateEndCritical(m_tid, kReadyThreadState, kNoThreadID);
481 wxASSERT( err == noErr ) ;
482
483 m_state = STATE_RUNNING;
484 ::ThreadEndCritical() ;
485 ::YieldToAnyThread() ;
486 return TRUE;
487}
488
489// static functions
490// ----------------
491wxThread *wxThread::This()
492{
493 wxMacStCritical critical ;
494
495 ThreadID current ;
496 OSErr err ;
497
498 err = MacGetCurrentThread( &current ) ;
499
500 for ( size_t i = 0 ; i < s_threads.Count() ; ++i )
501 {
502 if ( ( (wxThread*) s_threads[i] )->GetId() == current )
503 return (wxThread*) s_threads[i] ;
504 }
505
506 wxLogSysError(_("Couldn't get the current thread pointer"));
507 return NULL;
508}
509
510bool wxThread::IsMain()
511{
512 ThreadID current ;
513 OSErr err ;
514
515 err = MacGetCurrentThread( &current ) ;
516 return current == gs_idMainThread;
517}
518
519#ifdef Yield
520#undef Yield
521#endif
522
523void wxThread::Yield()
524{
525 ::YieldToAnyThread() ;
526}
527
528void wxThread::Sleep(unsigned long milliseconds)
529{
530 clock_t start = clock();
531 do
532 {
533 YieldToAnyThread();
534 } while( clock() - start < milliseconds / 1000.0 * CLOCKS_PER_SEC ) ;
535}
536
537int wxThread::GetCPUCount()
538{
539 // we will use whatever MP API will be used for the new MP Macs
540 return 1;
541}
542
543unsigned long wxThread::GetCurrentId()
544{
545 ThreadID current ;
546 MacGetCurrentThread( &current ) ;
547 return (unsigned long)current;
548}
549
550bool wxThread::SetConcurrency(size_t level)
551{
552 wxASSERT_MSG( IsMain(), _T("should only be called from the main thread") );
553
554 // ok only for the default one
555 if ( level == 0 )
556 return 0;
557
558 // how many CPUs have we got?
559 if ( GetCPUCount() == 1 )
560 {
561 // don't bother with all this complicated stuff - on a single
562 // processor system it doesn't make much sense anyhow
563 return level == 1;
564 }
565
566 return TRUE ;
567}
568
569// ctor and dtor
570// -------------
571
572wxThread::wxThread(wxThreadKind kind)
573{
574 m_internal = new wxThreadInternal();
575
576 m_isDetached = kind == wxTHREAD_DETACHED;
577 s_threads.Add( (void*) this ) ;
578}
579
580wxThread::~wxThread()
581{
582 s_threads.Remove( (void*) this ) ;
583 if (m_internal != NULL) {
584 delete m_internal;
585 m_internal = NULL;
586 }
587}
588
589// create/start thread
590// -------------------
591
592wxThreadError wxThread::Create(unsigned int stackSize)
593{
594 wxCriticalSectionLocker lock(m_critsect);
595
596 if ( !m_internal->Create(this, stackSize) )
597 return wxTHREAD_NO_RESOURCE;
598
599 return wxTHREAD_NO_ERROR;
600}
601
602wxThreadError wxThread::Run()
603{
604 wxCriticalSectionLocker lock(m_critsect);
605
606 if ( m_internal->GetState() != STATE_NEW )
607 {
608 // actually, it may be almost any state at all, not only STATE_RUNNING
609 return wxTHREAD_RUNNING;
610 }
611
612 // the thread has just been created and is still suspended - let it run
613 return Resume();
614}
615
616// suspend/resume thread
617// ---------------------
618
619wxThreadError wxThread::Pause()
620{
621 wxCriticalSectionLocker lock(m_critsect);
622
623 return m_internal->Suspend() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
624}
625
626wxThreadError wxThread::Resume()
627{
628 wxCriticalSectionLocker lock(m_critsect);
629
630 return m_internal->Resume() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
631}
632
633// stopping thread
634// ---------------
635
636wxThread::ExitCode wxThread::Wait()
637{
638 // although under MacOS we can wait for any thread, it's an error to
639 // wait for a detached one in wxWin API
640 wxCHECK_MSG( !IsDetached(), (ExitCode)-1,
641 _T("can't wait for detached thread") );
642
643 ExitCode rc = (ExitCode)-1;
644
645 (void)Delete(&rc);
646
647 m_internal->Free();
648
649 return rc;
650}
651
652wxThreadError wxThread::Delete(ExitCode *pRc)
653{
654 ExitCode rc = 0;
655
656 // Delete() is always safe to call, so consider all possible states
657
658 // has the thread started to run?
659 bool shouldResume = FALSE;
660
661 {
662 wxCriticalSectionLocker lock(m_critsect);
663
664 if ( m_internal->GetState() == STATE_NEW )
665 {
666 // WinThreadStart() will see it and terminate immediately
667 m_internal->SetState(STATE_EXITED);
668
669 shouldResume = TRUE;
670 }
671 }
672
673 // is the thread paused?
674 if ( shouldResume || IsPaused() )
675 Resume();
676
677 // does is still run?
678 if ( IsRunning() )
679 {
680 if ( IsMain() )
681 {
682 // set flag for wxIsWaitingForThread()
683 gs_waitingForThread = TRUE;
684
685#if wxUSE_GUI
686 wxBeginBusyCursor();
687#endif // wxUSE_GUI
688 }
689
690 // ask the thread to terminate
691 {
692 wxCriticalSectionLocker lock(m_critsect);
693
694 m_internal->Cancel();
695 }
696
697#if wxUSE_GUI
698 // simply wait for the thread to terminate
699 while( TestDestroy() )
700 {
701 ::YieldToAnyThread() ;
702 }
703#else // !wxUSE_GUI
704 // simply wait for the thread to terminate
705 while( TestDestroy() )
706 {
707 ::YieldToAnyThread() ;
708 }
709#endif // wxUSE_GUI/!wxUSE_GUI
710
711 if ( IsMain() )
712 {
713 gs_waitingForThread = FALSE;
714
715#if wxUSE_GUI
716 wxEndBusyCursor();
717#endif // wxUSE_GUI
718 }
719 }
720
721 if ( IsDetached() )
722 {
723 // if the thread exits normally, this is done in WinThreadStart, but in
724 // this case it would have been too early because
725 // MsgWaitForMultipleObject() would fail if the therad handle was
726 // closed while we were waiting on it, so we must do it here
727 delete this;
728 }
729
730 if ( pRc )
731 *pRc = rc;
732
733 return rc == (ExitCode)-1 ? wxTHREAD_MISC_ERROR : wxTHREAD_NO_ERROR;
734}
735
736wxThreadError wxThread::Kill()
737{
738 if ( !IsRunning() )
739 return wxTHREAD_NOT_RUNNING;
740
741// if ( !::TerminateThread(m_internal->GetHandle(), (DWORD)-1) )
742 {
743 wxLogSysError(_("Couldn't terminate thread"));
744
745 return wxTHREAD_MISC_ERROR;
746 }
747
748 m_internal->Free();
749
750 if ( IsDetached() )
751 {
752 delete this;
753 }
754
755 return wxTHREAD_NO_ERROR;
756}
757
758void wxThread::Exit(ExitCode status)
759{
760 m_internal->Free();
761
762 if ( IsDetached() )
763 {
764 delete this;
765 }
766
767 m_internal->SetResult( status ) ;
768
769/*
770#if defined(__VISUALC__) || (defined(__BORLANDC__) && (__BORLANDC__ >= 0x500))
771 _endthreadex((unsigned)status);
772#else // !VC++
773 ::ExitThread((DWORD)status);
774#endif // VC++/!VC++
775*/
776 wxFAIL_MSG(wxT("Couldn't return from ExitThread()!"));
777}
778
779// priority setting
780// ----------------
781
782// since all these calls are execute cooperatively we don't have to use the critical section
783
784void wxThread::SetPriority(unsigned int prio)
785{
786 m_internal->SetPriority(prio);
787}
788
789unsigned int wxThread::GetPriority() const
790{
791 return m_internal->GetPriority();
792}
793
794unsigned long wxThread::GetId() const
795{
796 return (unsigned long)m_internal->GetId();
797}
798
799bool wxThread::IsRunning() const
800{
801 return m_internal->GetState() == STATE_RUNNING;
802}
803
804bool wxThread::IsAlive() const
805{
806 return (m_internal->GetState() == STATE_RUNNING) ||
807 (m_internal->GetState() == STATE_PAUSED);
808}
809
810bool wxThread::IsPaused() const
811{
812 return m_internal->GetState() == STATE_PAUSED;
813}
814
815bool wxThread::TestDestroy()
816{
817 return m_internal->GetState() == STATE_CANCELED;
818}
819
820// ----------------------------------------------------------------------------
821// Automatic initialization for thread module
822// ----------------------------------------------------------------------------
823
824class wxThreadModule : public wxModule
825{
826public:
827 virtual bool OnInit();
828 virtual void OnExit();
829
830private:
831 DECLARE_DYNAMIC_CLASS(wxThreadModule)
832};
833
834IMPLEMENT_DYNAMIC_CLASS(wxThreadModule, wxModule)
835
836bool wxThreadModule::OnInit()
837{
838 long response;
839 bool hasThreadManager ;
840 hasThreadManager = Gestalt( gestaltThreadMgrAttr, &response) == noErr && response & 1;
841#if !TARGET_CARBON
842#if GENERATINGCFM
843 // verify presence of shared library
844 hasThreadManager = hasThreadManager && ((Ptr)NewThread != (Ptr)kUnresolvedCFragSymbolAddress);
845#endif
846#endif
847 if ( !hasThreadManager )
848 {
849 wxLogSysError( wxT("Thread Support is not available on this System") );
850 return FALSE ;
851 }
852
853 // no error return for GetCurrentThreadId()
854 MacGetCurrentThread( &gs_idMainThread ) ;
855
856 return TRUE;
857}
858
859void wxThreadModule::OnExit()
860{
861}
862
863// ----------------------------------------------------------------------------
864// under MacOS we don't have currently preemptive threads, so any thread may access
865// the GUI at any time
866// ----------------------------------------------------------------------------
867
868void WXDLLEXPORT wxMutexGuiEnter()
869{
870}
871
872void WXDLLEXPORT wxMutexGuiLeave()
873{
874}
875
876void WXDLLEXPORT wxMutexGuiLeaveOrEnter()
877{
878}
879
880bool WXDLLEXPORT wxGuiOwnedByMainThread()
881{
882 return false ;
883}
884
885// wake up the main thread
886void WXDLLEXPORT wxWakeUpMainThread()
887{
888 wxMacWakeUp() ;
889}
890
891bool WXDLLEXPORT wxIsWaitingForThread()
892{
893 return false ;
894}
895
896#include "wx/thrimpl.cpp"
897
898#endif // wxUSE_THREADS