Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/console/src/core/TclThreadManager.cc @ 1319

Last change on this file since 1319 was 1276, checked in by landauf, 17 years ago

someone likes to chat? well, I do. here's a new IRC client. it connects directly into our irc.datacore.ch / #orxonox channel with a random nickname. use the "say text" command to write text into the channel. there are some more commands without a shortcut, type "IRC" and tab.
please update your media directory.

for some reason, irc-client and telnet-remote don't work together. please use only one feature at the same time. I'm not sure if it's a bug in TCL or in my TclThreadManager class. The problem also occurs with irc or telnet in parallel with another busy tcl-thread… strange thing.

File size: 24.1 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Fabian 'x3n' Landau
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29#include <iostream>
30#include <string>
31
32#include <boost/thread/thread.hpp>
33#include <boost/bind.hpp>
34
35#include <OgreTimer.h>
36
37#include "CoreIncludes.h"
38#include "ConsoleCommand.h"
39#include "CommandExecutor.h"
40#include "Debug.h"
41#include "TclBind.h"
42#include "TclThreadManager.h"
43#include "util/Convert.h"
44
45#define TCLTHREADMANAGER_MAX_QUEUE_LENGTH 100
46#define TCLTHREADMANAGER_MAX_CPU_USAGE 0.50
47
48namespace orxonox
49{
50    ConsoleCommandShortcutGeneric(tclexecute, createExecutor(createFunctor(&TclThreadManager::execute), "tclexecute", AccessLevel::None));
51    ConsoleCommandShortcutGeneric(tclquery,   createExecutor(createFunctor(&TclThreadManager::query),   "tclquery",   AccessLevel::None));
52    ConsoleCommand(TclThreadManager, create,    AccessLevel::None, false);
53    ConsoleCommand(TclThreadManager, destroy,   AccessLevel::None, false);
54    ConsoleCommand(TclThreadManager, execute,   AccessLevel::None, false);
55    ConsoleCommand(TclThreadManager, query,     AccessLevel::None, false);
56    ConsoleCommand(TclThreadManager, status,    AccessLevel::None, false);
57    ConsoleCommand(TclThreadManager, dump,      AccessLevel::None, false);
58    ConsoleCommand(TclThreadManager, flush,     AccessLevel::None, false);
59
60    TclThreadManager* instance_tclthreadmanager = &TclThreadManager::getInstance();
61
62    TclThreadManager::TclThreadManager()
63    {
64        RegisterRootObject(TclThreadManager);
65
66        this->threadCounter_ = 0;
67        this->orxonoxInterpreterBundle_.id_ = 0;
68        this->orxonoxInterpreterBundle_.interpreter_ = TclBind::getInstance().getTclInterpreter();
69        this->threadID_ = boost::this_thread::get_id();
70    }
71
72    TclThreadManager& TclThreadManager::getInstance()
73    {
74        static TclThreadManager instance;
75        return instance;
76    }
77
78    unsigned int TclThreadManager::create()
79    {
80        boost::mutex::scoped_lock bundles_lock(TclThreadManager::getInstance().bundlesMutex_);
81        TclThreadManager::getInstance().threadCounter_++;
82        std::string name = getConvertedValue<unsigned int, std::string>(TclThreadManager::getInstance().threadCounter_);
83
84        TclInterpreterBundle* bundle = new TclInterpreterBundle;
85        bundle->id_ = TclThreadManager::getInstance().threadCounter_;
86        bundle->interpreter_ = TclThreadManager::getInstance().createNewTclInterpreter(name);
87        bundle->interpreterName_ = name;
88        bundle->running_ = true;
89        bundle->finished_ = true;
90
91        TclThreadManager::getInstance().interpreterBundles_[TclThreadManager::getInstance().threadCounter_] = bundle;
92        COUT(0) << "Created new Tcl-interpreter with ID " << TclThreadManager::getInstance().threadCounter_ << std::endl;
93        return TclThreadManager::getInstance().threadCounter_;
94    }
95
96    unsigned int TclThreadManager::createID(unsigned int threadID)
97    {
98        unsigned int temp = TclThreadManager::getInstance().threadCounter_;
99        TclThreadManager::getInstance().threadCounter_ = threadID - 1;
100        TclThreadManager::create();
101        TclThreadManager::getInstance().threadCounter_ = temp;
102        return threadID;
103    }
104
105    void TclThreadManager::destroy(unsigned int threadID)
106    {
107        TclInterpreterBundle* bundle = TclThreadManager::getInstance().getInterpreterBundle(threadID);
108        if (bundle)
109        {
110            {
111                boost::mutex::scoped_lock running_lock(bundle->runningMutex_);
112                bundle->running_ = false;
113            }
114            while (true)
115            {
116                {
117                    boost::mutex::scoped_lock finished_lock(bundle->finishedMutex_);
118                    if (bundle->finished_)
119                    {
120                        boost::mutex::scoped_lock bundles_lock(TclThreadManager::getInstance().bundlesMutex_);
121                        boost::mutex::scoped_lock interpreter_lock(bundle->interpreterMutex_, boost::defer_lock_t());
122                        try
123                        {
124                            while (!interpreter_lock.try_lock())
125                            {
126                                TclThreadManager::getInstance().orxonoxEvalCondition_.notify_one();
127                                boost::this_thread::yield();
128                            }
129                        } catch (...) {}
130                        delete bundle->interpreter_;
131                        delete bundle;
132                        TclThreadManager::getInstance().interpreterBundles_.erase(threadID);
133                        break;
134                    }
135                }
136
137                TclThreadManager::getInstance().orxonoxEvalCondition_.notify_one();
138                boost::this_thread::yield();
139            }
140
141            COUT(0) << "Destroyed Tcl-interpreter with ID " << threadID << std::endl;
142        }
143    }
144
145    void TclThreadManager::execute(unsigned int threadID, const std::string& _command)
146    {
147        std::string command = stripEnclosingBraces(_command);
148
149        if (threadID == 0)
150            TclThreadManager::getInstance().pushCommandToQueue(command);
151        else
152            TclThreadManager::getInstance().pushCommandToQueue(threadID, command);
153    }
154
155    std::string TclThreadManager::query(unsigned int threadID, const std::string& command)
156    {
157        return TclThreadManager::getInstance().evalQuery(TclThreadManager::getInstance().orxonoxInterpreterBundle_.id_, threadID, command);
158    }
159
160    void TclThreadManager::status()
161    {
162        COUT(0) << "Thread ID" << '\t' << "Queue size" << '\t' << "State" << std::endl;
163
164        std::string output = "Orxonox";
165        output += "\t\t";
166        {
167            boost::mutex::scoped_lock queue_lock(TclThreadManager::getInstance().orxonoxInterpreterBundle_.queueMutex_);
168            output += getConvertedValue<unsigned int, std::string>(TclThreadManager::getInstance().orxonoxInterpreterBundle_.queue_.size());
169        }
170        output += "\t\t";
171        output += "busy";
172        COUT(0) << output << std::endl;
173
174        boost::mutex::scoped_lock bundles_lock(TclThreadManager::getInstance().bundlesMutex_);
175        for (std::map<unsigned int, TclInterpreterBundle*>::const_iterator it = TclThreadManager::getInstance().interpreterBundles_.begin(); it != TclThreadManager::getInstance().interpreterBundles_.end(); ++it)
176        {
177            std::string output = getConvertedValue<unsigned int, std::string>((*it).first);
178            output += "\t\t";
179            {
180                boost::mutex::scoped_lock queue_lock((*it).second->queueMutex_);
181                output += getConvertedValue<unsigned int, std::string>((*it).second->queue_.size());
182            }
183            output += "\t\t";
184            {
185                boost::mutex::scoped_lock interpreter_lock((*it).second->interpreterMutex_, boost::defer_lock_t());
186                if (interpreter_lock.try_lock())
187                    output += "ready";
188                else
189                    output += "busy";
190            }
191            COUT(0) << output << std::endl;
192        }
193    }
194
195    void TclThreadManager::dump(unsigned int threadID)
196    {
197        TclInterpreterBundle* bundle = 0;
198        if (threadID == 0)
199        {
200            bundle = &TclThreadManager::getInstance().orxonoxInterpreterBundle_;
201            COUT(0) << "Queue dump of Orxonox:" << std::endl;
202        }
203        else
204        {
205            if (bundle = TclThreadManager::getInstance().getInterpreterBundle(threadID))
206            {
207                COUT(0) << "Queue dump of Tcl-thread " << threadID << ":" << std::endl;
208            }
209            else
210                return;
211        }
212
213        boost::mutex::scoped_lock queue_lock(bundle->queueMutex_);
214        unsigned int index = 0;
215        for (std::list<std::string>::const_iterator it = bundle->queue_.begin(); it != bundle->queue_.end(); ++it)
216        {
217            index++;
218            COUT(0) << index << ": " << (*it) << std::endl;
219        }
220    }
221
222    void TclThreadManager::flush(unsigned int threadID)
223    {
224        TclInterpreterBundle* bundle = 0;
225        if (threadID == 0)
226            bundle = &TclThreadManager::getInstance().orxonoxInterpreterBundle_;
227        else
228            if (!(bundle = TclThreadManager::getInstance().getInterpreterBundle(threadID)))
229                return;
230
231        boost::mutex::scoped_lock queue_lock(bundle->queueMutex_);
232        bundle->queue_.clear();
233        if (threadID == 0)
234        {
235            COUT(0) << "Flushed queue of Orxonox Tcl-interpreter." << std::endl;
236        }
237        else
238        {
239            COUT(0) << "Flushed queue of Tcl-interpreter " << threadID << "." << std::endl;
240        }
241    }
242
243    void TclThreadManager::tcl_execute(Tcl::object const &args)
244    {
245        TclThreadManager::getInstance().pushCommandToQueue(stripEnclosingBraces(args.get()));
246    }
247
248    std::string TclThreadManager::tcl_query(int querierID, Tcl::object const &args)
249    {
250        return TclThreadManager::getInstance().evalQuery((unsigned int)querierID, stripEnclosingBraces(args.get()));
251    }
252
253    std::string TclThreadManager::tcl_crossquery(int querierID, int threadID, Tcl::object const &args)
254    {
255        return TclThreadManager::getInstance().evalQuery((unsigned int)querierID, (unsigned int)threadID, stripEnclosingBraces(args.get()));
256    }
257
258    bool TclThreadManager::tcl_running(int threadID)
259    {
260        TclInterpreterBundle* bundle = TclThreadManager::getInstance().getInterpreterBundle((unsigned int)threadID);
261        if (bundle)
262        {
263            boost::mutex::scoped_lock running_lock(bundle->runningMutex_);
264            return bundle->running_;
265        }
266        return false;
267    }
268
269    Tcl::interpreter* TclThreadManager::createNewTclInterpreter(const std::string& threadID)
270    {
271        Tcl::interpreter* i = 0;
272        i = new Tcl::interpreter(TclBind::getInstance().getTclLibPath());
273
274        try
275        {
276            i->def("orxonox::query", TclThreadManager::tcl_query, Tcl::variadic());
277            i->def("orxonox::crossquery", TclThreadManager::tcl_crossquery, Tcl::variadic());
278            i->def("orxonox::execute", TclThreadManager::tcl_execute, Tcl::variadic());
279            i->def("orxonox::running", TclThreadManager::tcl_running);
280
281            i->def("execute", TclThreadManager::tcl_execute, Tcl::variadic());
282            i->eval("proc query args { orxonox::query " + threadID + " $args }");
283            i->eval("proc crossquery {id args} { orxonox::crossquery " + threadID + " $id $args }");
284            i->eval("set id " + threadID);
285
286            i->eval("rename exit tcl::exit");
287            i->eval("proc exit {} { orxonox TclThreadManager destroy " + threadID + " }");
288
289            i->eval("redef_puts");
290
291//            i->eval("rename while tcl::while");
292//            i->eval("proc while {test command} { tcl::while {[uplevel 1 expr $test]} {uplevel 1 $command} }"); // (\"$test\" && [orxonox::running " + threadID + "]])
293//            i->eval("rename for tcl::for");
294//            i->eval("proc for {start test next command} { uplevel tcl::for \"$start\" \"$test\" \"$next\" \"$command\" }");
295        }
296        catch (Tcl::tcl_error const &e)
297        {   COUT(1) << "Tcl error while creating Tcl-interpreter (" << threadID << "): " << e.what() << std::endl;   }
298        catch (std::exception const &e)
299        {   COUT(1) << "Error while creating Tcl-interpreter (" << threadID << "): " << e.what() << std::endl;   }
300
301        return i;
302    }
303
304    TclInterpreterBundle* TclThreadManager::getInterpreterBundle(unsigned int threadID)
305    {
306        boost::mutex::scoped_lock bundles_lock(this->bundlesMutex_);
307        std::map<unsigned int, TclInterpreterBundle*>::iterator it = this->interpreterBundles_.find(threadID);
308        if (it != this->interpreterBundles_.end())
309        {
310            return (*it).second;
311        }
312        else
313        {
314            this->error("Error: No Tcl-interpreter with ID " + getConvertedValue<unsigned int, std::string>(threadID) + " existing.");
315            return 0;
316        }
317    }
318
319    std::string TclThreadManager::dumpList(const std::list<unsigned int>& list)
320    {
321        std::string output = "";
322        for (std::list<unsigned int>::const_iterator it = list.begin(); it != list.end(); ++it)
323        {
324            if (it != list.begin())
325                output += " ";
326
327            output += getConvertedValue<unsigned int, std::string>(*it);
328        }
329        return output;
330    }
331
332    void TclThreadManager::error(const std::string& error)
333    {
334        if (boost::this_thread::get_id() != this->threadID_)
335        {
336            boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_.queueMutex_);
337            if (this->orxonoxInterpreterBundle_.queue_.size() >= TCLTHREADMANAGER_MAX_QUEUE_LENGTH)
338            {
339                boost::this_thread::yield();
340                return;
341            }
342        }
343
344        this->forceCommandToFrontOfQueue("error " + error);
345    }
346
347    void TclThreadManager::debug(const std::string& error)
348    {
349        if (boost::this_thread::get_id() != this->threadID_)
350        {
351            boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_.queueMutex_);
352            if (this->orxonoxInterpreterBundle_.queue_.size() >= TCLTHREADMANAGER_MAX_QUEUE_LENGTH)
353            {
354                boost::this_thread::yield();
355                return;
356            }
357        }
358
359        this->forceCommandToFrontOfQueue("debug " + error);
360    }
361
362    void TclThreadManager::pushCommandToQueue(const std::string& command)
363    {
364        boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_.queueMutex_);
365        while (this->orxonoxInterpreterBundle_.queue_.size() >= TCLTHREADMANAGER_MAX_QUEUE_LENGTH)
366            this->fullQueueCondition_.wait(queue_lock);
367
368        this->orxonoxInterpreterBundle_.queue_.push_back(command);
369    }
370
371    void TclThreadManager::forceCommandToFrontOfQueue(const std::string& command)
372    {
373        boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_.queueMutex_);
374        this->orxonoxInterpreterBundle_.queue_.push_front(command);
375    }
376
377    std::string TclThreadManager::popCommandFromQueue()
378    {
379        boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_.queueMutex_);
380        std::string temp = this->orxonoxInterpreterBundle_.queue_.front();
381        this->orxonoxInterpreterBundle_.queue_.pop_front();
382        this->fullQueueCondition_.notify_one();
383        return temp;
384    }
385
386    bool TclThreadManager::queueIsEmpty()
387    {
388        boost::mutex::scoped_lock queue_lock(this->orxonoxInterpreterBundle_.queueMutex_);
389        return this->orxonoxInterpreterBundle_.queue_.empty();
390    }
391
392    void TclThreadManager::pushCommandToQueue(unsigned int threadID, const std::string& command)
393    {
394        TclInterpreterBundle* bundle = this->getInterpreterBundle(threadID);
395        if (bundle)
396        {
397            boost::mutex::scoped_lock queue_lock(bundle->queueMutex_);
398            if (bundle->queue_.size() >= TCLTHREADMANAGER_MAX_QUEUE_LENGTH)
399            {
400                this->error("Error: Queue of Tcl-interpreter " + getConvertedValue<unsigned int, std::string>(threadID) + " is full, couldn't add command.");
401                return;
402            }
403
404            bundle->queue_.push_back(command);
405        }
406    }
407
408    std::string TclThreadManager::popCommandFromQueue(unsigned int threadID)
409    {
410        TclInterpreterBundle* bundle = this->getInterpreterBundle(threadID);
411        if (bundle)
412        {
413            boost::mutex::scoped_lock queue_lock(bundle->queueMutex_);
414            std::string temp = bundle->queue_.front();
415            bundle->queue_.pop_front();
416            return temp;
417        }
418        return "";
419    }
420
421    bool TclThreadManager::queueIsEmpty(unsigned int threadID)
422    {
423        TclInterpreterBundle* bundle = this->getInterpreterBundle(threadID);
424        if (bundle)
425        {
426            boost::mutex::scoped_lock queue_lock(bundle->queueMutex_);
427            return bundle->queue_.empty();
428        }
429        return true;
430    }
431
432    bool TclThreadManager::updateQueriersList(TclInterpreterBundle* querier, TclInterpreterBundle* target)
433    {
434        if (querier == target)
435            return false;
436
437        boost::mutex::scoped_lock queriers_lock(target->queriersMutex_);
438
439        {
440            boost::mutex::scoped_lock queriers_lock(querier->queriersMutex_);
441            target->queriers_.insert(target->queriers_.end(), querier->queriers_.begin(), querier->queriers_.end());
442        }
443
444        target->queriers_.insert(target->queriers_.end(), querier->id_);
445
446        if (std::find(target->queriers_.begin(), target->queriers_.end(), target->id_) != target->queriers_.end())
447        {
448            this->error("Error: Circular query (" + this->dumpList(target->queriers_) + " -> " + getConvertedValue<unsigned int, std::string>(target->id_) + "), couldn't query Tcl-interpreter with ID " + getConvertedValue<unsigned int, std::string>(target->id_) + " from other interpreter with ID " + getConvertedValue<unsigned int, std::string>(querier->id_) + ".");
449            return false;
450        }
451
452        return true;
453    }
454
455    std::string TclThreadManager::evalQuery(unsigned int querierID, const std::string& command)
456    {
457        TclInterpreterBundle* querier = this->getInterpreterBundle(querierID);
458        std::string output = "";
459        if (querier)
460        {
461            if (this->updateQueriersList(querier, &this->orxonoxInterpreterBundle_))
462            {
463                boost::mutex::scoped_lock interpreter_lock(this->orxonoxInterpreterBundle_.interpreterMutex_);
464                this->orxonoxEvalCondition_.wait(interpreter_lock);
465
466                if (!CommandExecutor::execute(command, false))
467                    this->error("Error: Can't execute command \"" + command + "\"!");
468
469                if (CommandExecutor::getLastEvaluation().hasReturnvalue())
470                    output = CommandExecutor::getLastEvaluation().getReturnvalue().toString();
471            }
472
473            boost::mutex::scoped_lock queriers_lock(this->orxonoxInterpreterBundle_.queriersMutex_);
474            this->orxonoxInterpreterBundle_.queriers_.clear();
475        }
476        return output;
477    }
478
479    std::string TclThreadManager::evalQuery(unsigned int querierID, unsigned int threadID, const std::string& command)
480    {
481        TclInterpreterBundle* target = 0;
482        if (threadID)
483            target = this->getInterpreterBundle(threadID);
484        else
485            target = &this->orxonoxInterpreterBundle_;
486
487        std::string output = "";
488        if (target)
489        {
490            TclInterpreterBundle* querier = 0;
491            if (querierID)
492                querier = this->getInterpreterBundle(querierID);
493            else
494                querier = &this->orxonoxInterpreterBundle_;
495
496            if (querier)
497            {
498                if (this->updateQueriersList(querier, target))
499                {
500                    boost::mutex::scoped_lock interpreter_lock(target->interpreterMutex_, boost::defer_lock_t());
501                    bool successfullyLocked = false;
502                    try
503                    {
504                        if (querierID == 0 || std::find(querier->queriers_.begin(), querier->queriers_.end(), (unsigned int)0) != querier->queriers_.end())
505                            successfullyLocked = interpreter_lock.try_lock();
506                        else
507                        {
508                            while (!interpreter_lock.try_lock())
509                                boost::this_thread::yield();
510
511                            successfullyLocked = true;
512                        }
513                    } catch (...) {}
514
515                    if (successfullyLocked)
516                    {
517                        this->debug("TclThread_query: " + command);
518                        try
519                        {   output = (std::string)target->interpreter_->eval(command);   }
520                        catch (Tcl::tcl_error const &e)
521                        {   this->error("Tcl error: " + (std::string)e.what());   }
522                        catch (std::exception const &e)
523                        {   this->error("Error while executing Tcl: " + (std::string)e.what());   }
524                    }
525                    else
526                    {
527                        this->error("Error: Couldn't query Tcl-interpreter with ID " + getConvertedValue<unsigned int, std::string>(threadID) + ", interpreter is busy right now.");
528                    }
529                }
530
531                boost::mutex::scoped_lock queriers_lock(target->queriersMutex_);
532                target->queriers_.clear();
533            }
534        }
535        return output;
536    }
537
538    void TclThreadManager::tick(float dt)
539    {
540        {
541            this->orxonoxEvalCondition_.notify_one();
542            boost::this_thread::yield();
543        }
544
545        {
546            boost::mutex::scoped_lock bundles_lock(this->bundlesMutex_);
547            for (std::map<unsigned int, TclInterpreterBundle*>::iterator it = this->interpreterBundles_.begin(); it != this->interpreterBundles_.end(); ++it)
548            {
549                boost::mutex::scoped_lock queue_lock((*it).second->queueMutex_);
550                if (!(*it).second->queue_.empty())
551                {
552                    std::string command = (*it).second->queue_.front();
553                    (*it).second->queue_.pop_front();
554                    {
555                        boost::mutex::scoped_lock finished_lock((*it).second->finishedMutex_);
556                        (*it).second->finished_ = false;
557                    }
558                    boost::thread(boost::bind(&tclThread, (*it).second, command));
559                }
560            }
561        }
562
563        {
564            boost::mutex::scoped_lock interpreter_lock(this->orxonoxInterpreterBundle_.interpreterMutex_);
565            unsigned long maxtime = (unsigned long)(dt * 1000000 * TCLTHREADMANAGER_MAX_CPU_USAGE);
566            Ogre::Timer timer;
567            while (!this->queueIsEmpty())
568            {
569                CommandExecutor::execute(this->popCommandFromQueue(), false);
570                if (timer.getMicroseconds() > maxtime)
571                    break;
572            }
573        }
574    }
575
576    void tclThread(TclInterpreterBundle* interpreterBundle, std::string command)
577    {
578        TclThreadManager::getInstance().debug("TclThread_execute: " + command);
579        boost::mutex::scoped_lock interpreter_lock(interpreterBundle->interpreterMutex_);
580        try
581        {
582            interpreterBundle->interpreter_->eval(command);
583        }
584        catch (Tcl::tcl_error const &e)
585        {
586            TclThreadManager::getInstance().error("Tcl (ID " + getConvertedValue<unsigned int, std::string>(interpreterBundle->id_) + ") error: " + e.what());
587        }
588        catch (std::exception const &e)
589        {
590            TclThreadManager::getInstance().error("Error while executing Tcl (ID " + getConvertedValue<unsigned int, std::string>(interpreterBundle->id_) + "): " + e.what());
591        }
592
593        boost::mutex::scoped_lock finished_lock(interpreterBundle->finishedMutex_);
594        interpreterBundle->finished_ = true;
595        interpreterBundle->finishedCondition_.notify_all();
596    }
597}
Note: See TracBrowser for help on using the repository browser.