Back to index

lightning-sunbird  0.9+nobinonly
nsToolkit.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsToolkit.h"
00039 #include "prmon.h"
00040 #include "prtime.h"
00041 #include "nsGUIEvent.h"
00042 #include "nsSwitchToUIThread.h"
00043 #include "plevent.h"
00044 #include "prprf.h"
00045 
00046 // 
00047 // Static thread local storage index of the Toolkit 
00048 // object associated with a given thread... 
00049 // 
00050 static PRUintn gToolkitTLSIndex = 0; 
00051 
00052 //-------------------------------------------------------------------------
00053 //
00054 // nsISupports implementation macro
00055 //
00056 //-------------------------------------------------------------------------
00057 NS_IMPL_THREADSAFE_ISUPPORTS1(nsToolkit, nsIToolkit)
00058 
00059 struct ThreadInterfaceData
00060 {
00061   void *data;
00062   thread_id waitingThread;
00063 };
00064 
00065 //
00066 // main for the message pump thread
00067 //
00068 PRBool gThreadState = PR_FALSE;
00069 
00070 struct ThreadInitInfo {
00071   PRMonitor *monitor;
00072   nsToolkit *toolkit;
00073 };
00074 
00075 void nsToolkit::RunPump(void* arg)
00076 {
00077   int32              code;
00078   char        portname[64];
00079   ThreadInterfaceData id;
00080 
00081   ThreadInitInfo *info = (ThreadInitInfo*)arg;
00082   PR_EnterMonitor(info->monitor);
00083 
00084   gThreadState = PR_TRUE;
00085 
00086   PR_Notify(info->monitor);
00087   PR_ExitMonitor(info->monitor);
00088 
00089   delete info;
00090 
00091   // system wide unique names
00092   PR_snprintf(portname, sizeof(portname), "event%lx", 
00093               (long unsigned) PR_GetCurrentThread());
00094 
00095   port_id event = create_port(100, portname);
00096 
00097   while(read_port(event, &code, &id, sizeof(id)) >= 0)
00098   {
00099     switch(code)
00100     {
00101       case WM_CALLMETHOD :
00102         {
00103           MethodInfo *mInfo = (MethodInfo *)id.data;
00104           mInfo->Invoke();
00105           if(id.waitingThread != 0)
00106             resume_thread(id.waitingThread);
00107           delete mInfo;
00108         }
00109         break;
00110       case 'natv' :  // native queue PLEvent
00111         {
00112           PREventQueue *queue = (PREventQueue *)id.data;
00113           PR_ProcessPendingEvents(queue);
00114         }
00115         break;
00116 
00117       default :
00118         printf("nsToolkit::RunPump - UNKNOWN EVENT\n");
00119         break;
00120     }
00121   }
00122 }
00123 
00124 //-------------------------------------------------------------------------
00125 //
00126 // constructor
00127 //
00128 //-------------------------------------------------------------------------
00129 nsToolkit::nsToolkit()  
00130 {
00131   localthread = false;
00132   mGuiThread  = NULL;
00133 }
00134 
00135 
00136 //-------------------------------------------------------------------------
00137 //
00138 // destructor
00139 //
00140 //-------------------------------------------------------------------------
00141 nsToolkit::~nsToolkit()
00142 {
00143   Kill();
00144   PR_SetThreadPrivate(gToolkitTLSIndex, nsnull);
00145 }
00146 
00147 void nsToolkit::Kill()
00148 {
00149   if(localthread)
00150   {
00151     GetInterface();
00152 
00153     // interrupt message flow
00154     close_port(eventport);
00155   }
00156 }
00157 
00158 //-------------------------------------------------------------------------
00159 //
00160 // Create a new thread and run the message pump in there
00161 //
00162 //-------------------------------------------------------------------------
00163 void nsToolkit::CreateUIThread()
00164 {
00165   PRMonitor *monitor = ::PR_NewMonitor();
00166        
00167   PR_EnterMonitor(monitor);
00168        
00169   ThreadInitInfo *ti = new ThreadInitInfo();
00170   if (ti)
00171   {
00172     ti->monitor = monitor;
00173     ti->toolkit = this;
00174   
00175     // create a gui thread
00176     mGuiThread = PR_CreateThread(PR_SYSTEM_THREAD,
00177                                    RunPump,
00178                                    (void*)ti,
00179                                    PR_PRIORITY_HIGH,
00180                                    PR_LOCAL_THREAD,
00181                                    PR_UNJOINABLE_THREAD,
00182                                    0);
00183 
00184     // wait for the gui thread to start
00185     while(gThreadState == PR_FALSE)
00186     {
00187       PR_Wait(monitor, PR_INTERVAL_NO_TIMEOUT);
00188     }
00189   }
00190     
00191   // at this point the thread is running
00192   PR_ExitMonitor(monitor);
00193   PR_DestroyMonitor(monitor);
00194 }
00195 
00196 
00197 //-------------------------------------------------------------------------
00198 //
00199 //
00200 //-------------------------------------------------------------------------
00201 NS_METHOD nsToolkit::Init(PRThread *aThread)
00202 {
00203   Kill();
00204 
00205   // Store the thread ID of the thread containing the message pump.  
00206   // If no thread is provided create one
00207   if (NULL != aThread) 
00208   {
00209     mGuiThread = aThread;
00210     localthread = false;
00211   } 
00212   else 
00213   {
00214     localthread = true;
00215 
00216     // create a thread where the message pump will run
00217     CreateUIThread();
00218   }
00219 
00220   cached = false;
00221 
00222   return NS_OK;
00223 }
00224 
00225 void nsToolkit::GetInterface()
00226 {
00227   if(! cached)
00228   {
00229     char portname[64];
00230 
00231     PR_snprintf(portname, sizeof(portname), "event%lx", 
00232                 (long unsigned) mGuiThread);
00233 
00234     eventport = find_port(portname);
00235 
00236     cached = true;
00237   }
00238 }
00239 
00240 void nsToolkit::CallMethod(MethodInfo *info)
00241 {
00242   ThreadInterfaceData id;
00243 
00244   GetInterface();
00245 
00246   id.data = info;
00247   id.waitingThread = find_thread(NULL);
00248   if(write_port(eventport, WM_CALLMETHOD, &id, sizeof(id)) == B_OK)
00249   {
00250     // semantics for CallMethod are that it should be synchronous
00251     suspend_thread(id.waitingThread);
00252   }
00253 }
00254 
00255 // to be used only from a BView or BWindow
00256 void nsToolkit::CallMethodAsync(MethodInfo *info)
00257 {
00258   ThreadInterfaceData id;
00259 
00260   GetInterface();
00261 
00262   id.data = info;
00263   id.waitingThread = 0;
00264        
00265   // Check message count to not exceed the port's capacity.
00266   // There seems to be a BeOS bug that allows more 
00267   // messages on a port than its capacity.
00268   port_info portinfo;
00269   if (get_port_info(eventport, &portinfo) != B_OK)
00270   {
00271     return;
00272   }
00273   
00274   if (port_count(eventport) < portinfo.capacity - 20) 
00275   {
00276     write_port_etc(eventport, WM_CALLMETHOD, &id, sizeof(id), B_TIMEOUT, 0);
00277   }
00278 }
00279 
00280 //------------------------------------------------------------------------- 
00281 // 
00282 // Return the nsIToolkit for the current thread.  If a toolkit does not 
00283 // yet exist, then one will be created... 
00284 // 
00285 //------------------------------------------------------------------------- 
00286 NS_METHOD NS_GetCurrentToolkit(nsIToolkit* *aResult) 
00287 { 
00288   nsIToolkit* toolkit = nsnull; 
00289   nsresult rv = NS_OK; 
00290   PRStatus status; 
00291 
00292   // Create the TLS index the first time through... 
00293   if (0 == gToolkitTLSIndex)  
00294   { 
00295     status = PR_NewThreadPrivateIndex(&gToolkitTLSIndex, NULL); 
00296     if (PR_FAILURE == status) 
00297     { 
00298       rv = NS_ERROR_FAILURE; 
00299     } 
00300   } 
00301 
00302   if (NS_SUCCEEDED(rv)) 
00303   { 
00304     toolkit = (nsIToolkit*)PR_GetThreadPrivate(gToolkitTLSIndex); 
00305 
00306     // 
00307     // Create a new toolkit for this thread... 
00308     // 
00309     if (!toolkit) 
00310     { 
00311       toolkit = new nsToolkit(); 
00312 
00313       if (!toolkit) 
00314       { 
00315         rv = NS_ERROR_OUT_OF_MEMORY; 
00316       } 
00317       else 
00318       { 
00319         NS_ADDREF(toolkit); 
00320         toolkit->Init(PR_GetCurrentThread()); 
00321         // 
00322         // The reference stored in the TLS is weak.  It is removed in the 
00323         // nsToolkit destructor... 
00324         // 
00325         PR_SetThreadPrivate(gToolkitTLSIndex, (void*)toolkit); 
00326       } 
00327     } 
00328     else 
00329     { 
00330       NS_ADDREF(toolkit); 
00331     } 
00332     *aResult = toolkit; 
00333   } 
00334 
00335   return rv; 
00336 }