eventq.cc revision 10153
12SN/A/* 21762SN/A * Copyright (c) 2000-2005 The Regents of The University of Michigan 35502Snate@binkert.org * Copyright (c) 2008 The Hewlett-Packard Development Company 49983Sstever@gmail.com * Copyright (c) 2013 Advanced Micro Devices, Inc. 52SN/A * All rights reserved. 62SN/A * 72SN/A * Redistribution and use in source and binary forms, with or without 82SN/A * modification, are permitted provided that the following conditions are 92SN/A * met: redistributions of source code must retain the above copyright 102SN/A * notice, this list of conditions and the following disclaimer; 112SN/A * redistributions in binary form must reproduce the above copyright 122SN/A * notice, this list of conditions and the following disclaimer in the 132SN/A * documentation and/or other materials provided with the distribution; 142SN/A * neither the name of the copyright holders nor the names of its 152SN/A * contributors may be used to endorse or promote products derived from 162SN/A * this software without specific prior written permission. 172SN/A * 182SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 192SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 202SN/A * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 212SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 222SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 232SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 242SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 252SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 262SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 272SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 282SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 292665Ssaidi@eecs.umich.edu * 302665Ssaidi@eecs.umich.edu * Authors: Steve Reinhardt 312665Ssaidi@eecs.umich.edu * Nathan Binkert 322665Ssaidi@eecs.umich.edu * Steve Raasch 332SN/A */ 342SN/A 355501Snate@binkert.org#include <cassert> 362SN/A#include <iostream> 372SN/A#include <string> 382SN/A#include <vector> 392SN/A 405502Snate@binkert.org#include "base/hashmap.hh" 415501Snate@binkert.org#include "base/misc.hh" 425501Snate@binkert.org#include "base/trace.hh" 431717SN/A#include "cpu/smt.hh" 448232Snate@binkert.org#include "debug/Config.hh" 455501Snate@binkert.org#include "sim/core.hh" 469356Snilay@cs.wisc.edu#include "sim/eventq_impl.hh" 472SN/A 482SN/Ausing namespace std; 492SN/A 509983Sstever@gmail.comTick simQuantum = 0; 519983Sstever@gmail.com 522SN/A// 539983Sstever@gmail.com// Main Event Queues 542SN/A// 559983Sstever@gmail.com// Events on these queues are processed at the *beginning* of each 562SN/A// cycle, before the pipeline simulation is performed. 572SN/A// 589983Sstever@gmail.comuint32_t numMainEventQueues = 0; 599983Sstever@gmail.comvector<EventQueue *> mainEventQueue; 609983Sstever@gmail.com__thread EventQueue *_curEventQueue = NULL; 619983Sstever@gmail.combool inParallelMode = false; 629983Sstever@gmail.com 639983Sstever@gmail.comEventQueue * 649983Sstever@gmail.comgetEventQueue(uint32_t index) 659983Sstever@gmail.com{ 669983Sstever@gmail.com while (numMainEventQueues <= index) { 679983Sstever@gmail.com numMainEventQueues++; 689983Sstever@gmail.com mainEventQueue.push_back( 699983Sstever@gmail.com new EventQueue(csprintf("MainEventQueue-%d", index))); 709983Sstever@gmail.com } 719983Sstever@gmail.com 729983Sstever@gmail.com return mainEventQueue[index]; 739983Sstever@gmail.com} 742SN/A 754017Sstever@eecs.umich.edu#ifndef NDEBUG 764016Sstever@eecs.umich.eduCounter Event::instanceCounter = 0; 774017Sstever@eecs.umich.edu#endif 784016Sstever@eecs.umich.edu 795768Snate@binkert.orgEvent::~Event() 805768Snate@binkert.org{ 815774Snate@binkert.org assert(!scheduled()); 827059Snate@binkert.org flags = 0; 835768Snate@binkert.org} 845768Snate@binkert.org 855768Snate@binkert.orgconst std::string 865768Snate@binkert.orgEvent::name() const 875768Snate@binkert.org{ 885768Snate@binkert.org#ifndef NDEBUG 895768Snate@binkert.org return csprintf("Event_%d", instance); 905768Snate@binkert.org#else 915768Snate@binkert.org return csprintf("Event_%x", (uintptr_t)this); 925768Snate@binkert.org#endif 935768Snate@binkert.org} 945768Snate@binkert.org 955768Snate@binkert.org 965602Snate@binkert.orgEvent * 975602Snate@binkert.orgEvent::insertBefore(Event *event, Event *curr) 985502Snate@binkert.org{ 995503Snate@binkert.org // Either way, event will be the top element in the 'in bin' list 1005502Snate@binkert.org // which is the pointer we need in order to look into the list, so 1015502Snate@binkert.org // we need to insert that into the bin list. 1025502Snate@binkert.org if (!curr || *event < *curr) { 1035502Snate@binkert.org // Insert the event before the current list since it is in the future. 1045502Snate@binkert.org event->nextBin = curr; 1055503Snate@binkert.org event->nextInBin = NULL; 1065502Snate@binkert.org } else { 1075502Snate@binkert.org // Since we're on the correct list, we need to point to the next list 1085502Snate@binkert.org event->nextBin = curr->nextBin; // curr->nextBin can now become stale 1095502Snate@binkert.org 1105503Snate@binkert.org // Insert event at the top of the stack 1115503Snate@binkert.org event->nextInBin = curr; 1125503Snate@binkert.org } 1135502Snate@binkert.org 1145503Snate@binkert.org return event; 1155502Snate@binkert.org} 1165502Snate@binkert.org 1172SN/Avoid 1182SN/AEventQueue::insert(Event *event) 1192SN/A{ 1205502Snate@binkert.org // Deal with the head case 1215502Snate@binkert.org if (!head || *event <= *head) { 1225602Snate@binkert.org head = Event::insertBefore(event, head); 1235502Snate@binkert.org return; 1245502Snate@binkert.org } 1252SN/A 1265502Snate@binkert.org // Figure out either which 'in bin' list we are on, or where a new list 1275502Snate@binkert.org // needs to be inserted 1285503Snate@binkert.org Event *prev = head; 1295503Snate@binkert.org Event *curr = head->nextBin; 1305503Snate@binkert.org while (curr && *curr < *event) { 1315503Snate@binkert.org prev = curr; 1325503Snate@binkert.org curr = curr->nextBin; 1335502Snate@binkert.org } 1342SN/A 1355503Snate@binkert.org // Note: this operation may render all nextBin pointers on the 1365503Snate@binkert.org // prev 'in bin' list stale (except for the top one) 1375602Snate@binkert.org prev->nextBin = Event::insertBefore(event, curr); 1385502Snate@binkert.org} 1392SN/A 1405602Snate@binkert.orgEvent * 1415602Snate@binkert.orgEvent::removeItem(Event *event, Event *top) 1425502Snate@binkert.org{ 1435503Snate@binkert.org Event *curr = top; 1445503Snate@binkert.org Event *next = top->nextInBin; 1455502Snate@binkert.org 1465503Snate@binkert.org // if we removed the top item, we need to handle things specially 1475503Snate@binkert.org // and just remove the top item, fixing up the next bin pointer of 1485503Snate@binkert.org // the new top item 1495503Snate@binkert.org if (event == top) { 1505503Snate@binkert.org if (!next) 1515503Snate@binkert.org return top->nextBin; 1525503Snate@binkert.org next->nextBin = top->nextBin; 1535503Snate@binkert.org return next; 1545503Snate@binkert.org } 1555503Snate@binkert.org 1565503Snate@binkert.org // Since we already checked the current element, we're going to 1575503Snate@binkert.org // keep checking event against the next element. 1585503Snate@binkert.org while (event != next) { 1595503Snate@binkert.org if (!next) 1605502Snate@binkert.org panic("event not found!"); 1615502Snate@binkert.org 1625503Snate@binkert.org curr = next; 1635503Snate@binkert.org next = next->nextInBin; 1642SN/A } 1655502Snate@binkert.org 1665503Snate@binkert.org // remove next from the 'in bin' list since it's what we're looking for 1675503Snate@binkert.org curr->nextInBin = next->nextInBin; 1685503Snate@binkert.org return top; 1692SN/A} 1702SN/A 1712SN/Avoid 1722SN/AEventQueue::remove(Event *event) 1732SN/A{ 1742SN/A if (head == NULL) 1755502Snate@binkert.org panic("event not found!"); 1762SN/A 1779983Sstever@gmail.com assert(event->queue == this); 1789983Sstever@gmail.com 1795502Snate@binkert.org // deal with an event on the head's 'in bin' list (event has the same 1805502Snate@binkert.org // time as the head) 1815502Snate@binkert.org if (*head == *event) { 1825602Snate@binkert.org head = Event::removeItem(event, head); 1832SN/A return; 1842SN/A } 1852SN/A 1865502Snate@binkert.org // Find the 'in bin' list that this event belongs on 1872SN/A Event *prev = head; 1885502Snate@binkert.org Event *curr = head->nextBin; 1895502Snate@binkert.org while (curr && *curr < *event) { 1902SN/A prev = curr; 1915502Snate@binkert.org curr = curr->nextBin; 1922SN/A } 1932SN/A 1945502Snate@binkert.org if (!curr || *curr != *event) 1955502Snate@binkert.org panic("event not found!"); 1965502Snate@binkert.org 1975503Snate@binkert.org // curr points to the top item of the the correct 'in bin' list, when 1985503Snate@binkert.org // we remove an item, it returns the new top item (which may be 1995502Snate@binkert.org // unchanged) 2005602Snate@binkert.org prev->nextBin = Event::removeItem(event, curr); 2012SN/A} 2022SN/A 2032667Sstever@eecs.umich.eduEvent * 2042SN/AEventQueue::serviceOne() 2052SN/A{ 20610153Sandreas@sandberg.pp.se std::lock_guard<EventQueue> lock(*this); 2075503Snate@binkert.org Event *event = head; 2085503Snate@binkert.org Event *next = head->nextInBin; 2095769Snate@binkert.org event->flags.clear(Event::Scheduled); 2105502Snate@binkert.org 2115503Snate@binkert.org if (next) { 2125503Snate@binkert.org // update the next bin pointer since it could be stale 2135503Snate@binkert.org next->nextBin = head->nextBin; 2145503Snate@binkert.org 2155503Snate@binkert.org // pop the stack 2165503Snate@binkert.org head = next; 2175503Snate@binkert.org } else { 2185502Snate@binkert.org // this was the only element on the 'in bin' list, so get rid of 2195502Snate@binkert.org // the 'in bin' list and point to the next bin list 2205503Snate@binkert.org head = head->nextBin; 2215502Snate@binkert.org } 2222SN/A 2232SN/A // handle action 2242667Sstever@eecs.umich.edu if (!event->squashed()) { 2259356Snilay@cs.wisc.edu // forward current cycle to the time when this event occurs. 2269356Snilay@cs.wisc.edu setCurTick(event->when()); 2279356Snilay@cs.wisc.edu 2282SN/A event->process(); 2292667Sstever@eecs.umich.edu if (event->isExitEvent()) { 2309328SAli.Saidi@ARM.com assert(!event->flags.isSet(Event::AutoDelete) || 2319328SAli.Saidi@ARM.com !event->flags.isSet(Event::IsMainQueue)); // would be silly 2322667Sstever@eecs.umich.edu return event; 2332667Sstever@eecs.umich.edu } 2342667Sstever@eecs.umich.edu } else { 2355769Snate@binkert.org event->flags.clear(Event::Squashed); 2362667Sstever@eecs.umich.edu } 2372SN/A 2385769Snate@binkert.org if (event->flags.isSet(Event::AutoDelete) && !event->scheduled()) 2392SN/A delete event; 2402667Sstever@eecs.umich.edu 2412667Sstever@eecs.umich.edu return NULL; 2422SN/A} 2432SN/A 244224SN/Avoid 245224SN/AEvent::serialize(std::ostream &os) 246224SN/A{ 247224SN/A SERIALIZE_SCALAR(_when); 248224SN/A SERIALIZE_SCALAR(_priority); 2495769Snate@binkert.org short _flags = flags; 2505769Snate@binkert.org SERIALIZE_SCALAR(_flags); 251224SN/A} 252224SN/A 253224SN/Avoid 254237SN/AEvent::unserialize(Checkpoint *cp, const string §ion) 255224SN/A{ 2569983Sstever@gmail.com} 2579983Sstever@gmail.com 2589983Sstever@gmail.comvoid 2599983Sstever@gmail.comEvent::unserialize(Checkpoint *cp, const string §ion, EventQueue *eventq) 2609983Sstever@gmail.com{ 2615695Snate@binkert.org if (scheduled()) 2629983Sstever@gmail.com eventq->deschedule(this); 263224SN/A 264224SN/A UNSERIALIZE_SCALAR(_when); 265224SN/A UNSERIALIZE_SCALAR(_priority); 266224SN/A 2675769Snate@binkert.org short _flags; 2685769Snate@binkert.org UNSERIALIZE_SCALAR(_flags); 2697452SLisa.Hsu@amd.com 2707452SLisa.Hsu@amd.com // Old checkpoints had no concept of the Initialized flag 2717452SLisa.Hsu@amd.com // so restoring from old checkpoints always fail. 2727452SLisa.Hsu@amd.com // Events are initialized on construction but original code 2737452SLisa.Hsu@amd.com // "flags = _flags" would just overwrite the initialization. 2747452SLisa.Hsu@amd.com // So, read in the checkpoint flags, but then set the Initialized 2757452SLisa.Hsu@amd.com // flag on top of it in order to avoid failures. 2767451SLisa.Hsu@amd.com assert(initialized()); 2775769Snate@binkert.org flags = _flags; 2787451SLisa.Hsu@amd.com flags.set(Initialized); 2795769Snate@binkert.org 2807452SLisa.Hsu@amd.com // need to see if original event was in a scheduled, unsquashed 2817452SLisa.Hsu@amd.com // state, but don't want to restore those flags in the current 2827452SLisa.Hsu@amd.com // object itself (since they aren't immediately true) 2835769Snate@binkert.org bool wasScheduled = flags.isSet(Scheduled) && !flags.isSet(Squashed); 2845769Snate@binkert.org flags.clear(Squashed | Scheduled); 285224SN/A 286224SN/A if (wasScheduled) { 287224SN/A DPRINTF(Config, "rescheduling at %d\n", _when); 2889983Sstever@gmail.com eventq->schedule(this, _when); 289224SN/A } 290224SN/A} 291224SN/A 2922SN/Avoid 293217SN/AEventQueue::serialize(ostream &os) 2942SN/A{ 295265SN/A std::list<Event *> eventPtrs; 296237SN/A 297237SN/A int numEvents = 0; 2985502Snate@binkert.org Event *nextBin = head; 2995502Snate@binkert.org while (nextBin) { 3005503Snate@binkert.org Event *nextInBin = nextBin; 3015502Snate@binkert.org 3025503Snate@binkert.org while (nextInBin) { 3035769Snate@binkert.org if (nextInBin->flags.isSet(Event::AutoSerialize)) { 3045502Snate@binkert.org eventPtrs.push_back(nextInBin); 3055502Snate@binkert.org paramOut(os, csprintf("event%d", numEvents++), 3065502Snate@binkert.org nextInBin->name()); 3075502Snate@binkert.org } 3085502Snate@binkert.org nextInBin = nextInBin->nextInBin; 3095503Snate@binkert.org } 3105502Snate@binkert.org 3115502Snate@binkert.org nextBin = nextBin->nextBin; 3122SN/A } 313237SN/A 314265SN/A SERIALIZE_SCALAR(numEvents); 315265SN/A 3165502Snate@binkert.org for (std::list<Event *>::iterator it = eventPtrs.begin(); 317265SN/A it != eventPtrs.end(); ++it) { 318265SN/A (*it)->nameOut(os); 319265SN/A (*it)->serialize(os); 320265SN/A } 3212SN/A} 3222SN/A 323237SN/Avoid 324237SN/AEventQueue::unserialize(Checkpoint *cp, const std::string §ion) 325237SN/A{ 326265SN/A int numEvents; 327265SN/A UNSERIALIZE_SCALAR(numEvents); 328265SN/A 329270SN/A std::string eventName; 330265SN/A for (int i = 0; i < numEvents; i++) { 331265SN/A // get the pointer value associated with the event 332270SN/A paramIn(cp, section, csprintf("event%d", i), eventName); 333265SN/A 334265SN/A // create the event based on its pointer value 335395SN/A Serializable::create(cp, eventName); 336237SN/A } 337237SN/A} 338237SN/A 3392SN/Avoid 3405501Snate@binkert.orgEventQueue::dump() const 3412SN/A{ 3422SN/A cprintf("============================================================\n"); 3437823Ssteve.reinhardt@amd.com cprintf("EventQueue Dump (cycle %d)\n", curTick()); 3442SN/A cprintf("------------------------------------------------------------\n"); 3452SN/A 3462SN/A if (empty()) 3472SN/A cprintf("<No Events>\n"); 3482SN/A else { 3495502Snate@binkert.org Event *nextBin = head; 3505502Snate@binkert.org while (nextBin) { 3515502Snate@binkert.org Event *nextInBin = nextBin; 3525503Snate@binkert.org while (nextInBin) { 3535503Snate@binkert.org nextInBin->dump(); 3545502Snate@binkert.org nextInBin = nextInBin->nextInBin; 3555503Snate@binkert.org } 3565502Snate@binkert.org 3575502Snate@binkert.org nextBin = nextBin->nextBin; 3582SN/A } 3592SN/A } 3602SN/A 3612SN/A cprintf("============================================================\n"); 3622SN/A} 3632SN/A 3645502Snate@binkert.orgbool 3655502Snate@binkert.orgEventQueue::debugVerify() const 3665502Snate@binkert.org{ 3675502Snate@binkert.org m5::hash_map<long, bool> map; 3685502Snate@binkert.org 3695502Snate@binkert.org Tick time = 0; 3705502Snate@binkert.org short priority = 0; 3715502Snate@binkert.org 3725502Snate@binkert.org Event *nextBin = head; 3735502Snate@binkert.org while (nextBin) { 3745503Snate@binkert.org Event *nextInBin = nextBin; 3755503Snate@binkert.org while (nextInBin) { 3765502Snate@binkert.org if (nextInBin->when() < time) { 3775502Snate@binkert.org cprintf("time goes backwards!"); 3785502Snate@binkert.org nextInBin->dump(); 3795502Snate@binkert.org return false; 3805502Snate@binkert.org } else if (nextInBin->when() == time && 3815502Snate@binkert.org nextInBin->priority() < priority) { 3825502Snate@binkert.org cprintf("priority inverted!"); 3835502Snate@binkert.org nextInBin->dump(); 3845502Snate@binkert.org return false; 3855502Snate@binkert.org } 3865502Snate@binkert.org 3875502Snate@binkert.org if (map[reinterpret_cast<long>(nextInBin)]) { 3885502Snate@binkert.org cprintf("Node already seen"); 3895502Snate@binkert.org nextInBin->dump(); 3905502Snate@binkert.org return false; 3915502Snate@binkert.org } 3925502Snate@binkert.org map[reinterpret_cast<long>(nextInBin)] = true; 3935502Snate@binkert.org 3945502Snate@binkert.org time = nextInBin->when(); 3955502Snate@binkert.org priority = nextInBin->priority(); 3965502Snate@binkert.org 3975502Snate@binkert.org nextInBin = nextInBin->nextInBin; 3985503Snate@binkert.org } 3995502Snate@binkert.org 4005502Snate@binkert.org nextBin = nextBin->nextBin; 4015502Snate@binkert.org } 4025502Snate@binkert.org 4035502Snate@binkert.org return true; 4045502Snate@binkert.org} 4055502Snate@binkert.org 4068648Snilay@cs.wisc.eduEvent* 4078648Snilay@cs.wisc.eduEventQueue::replaceHead(Event* s) 4088648Snilay@cs.wisc.edu{ 4098648Snilay@cs.wisc.edu Event* t = head; 4108648Snilay@cs.wisc.edu head = s; 4118648Snilay@cs.wisc.edu return t; 4128648Snilay@cs.wisc.edu} 4138648Snilay@cs.wisc.edu 4141019SN/Avoid 4151019SN/AdumpMainQueue() 4161019SN/A{ 4179983Sstever@gmail.com for (uint32_t i = 0; i < numMainEventQueues; ++i) { 4189983Sstever@gmail.com mainEventQueue[i]->dump(); 4199983Sstever@gmail.com } 4201019SN/A} 4211019SN/A 4222SN/A 4232SN/Aconst char * 4245336Shines@cs.fsu.eduEvent::description() const 4252SN/A{ 4262SN/A return "generic"; 4272SN/A} 4282SN/A 4292SN/Avoid 4302SN/AEvent::trace(const char *action) 4312SN/A{ 4322SN/A // This DPRINTF is unconditional because calls to this function 4332SN/A // are protected by an 'if (DTRACE(Event))' in the inlined Event 4342SN/A // methods. 4352SN/A // 4362SN/A // This is just a default implementation for derived classes where 4372SN/A // it's not worth doing anything special. If you want to put a 4382SN/A // more informative message in the trace, override this method on 4392SN/A // the particular subclass where you have the information that 4402SN/A // needs to be printed. 4412SN/A DPRINTFN("%s event %s @ %d\n", description(), action, when()); 4422SN/A} 4432SN/A 4442SN/Avoid 4455501Snate@binkert.orgEvent::dump() const 4462SN/A{ 4475501Snate@binkert.org cprintf("Event %s (%s)\n", name(), description()); 4485769Snate@binkert.org cprintf("Flags: %#x\n", flags); 4495501Snate@binkert.org#ifdef EVENTQ_DEBUG 4505501Snate@binkert.org cprintf("Created: %d\n", whenCreated); 4512SN/A#endif 4522SN/A if (scheduled()) { 4535501Snate@binkert.org#ifdef EVENTQ_DEBUG 4545501Snate@binkert.org cprintf("Scheduled at %d\n", whenScheduled); 4552SN/A#endif 4561019SN/A cprintf("Scheduled for %d, priority %d\n", when(), _priority); 4575501Snate@binkert.org } else { 4581019SN/A cprintf("Not Scheduled\n"); 4592SN/A } 4602SN/A} 4617063Snate@binkert.org 4627063Snate@binkert.orgEventQueue::EventQueue(const string &n) 4639983Sstever@gmail.com : objName(n), head(NULL), _curTick(0), 4649983Sstever@gmail.com async_queue_mutex(new std::mutex()) 4659983Sstever@gmail.com{ 4669983Sstever@gmail.com} 4679983Sstever@gmail.com 4689983Sstever@gmail.comvoid 4699983Sstever@gmail.comEventQueue::asyncInsert(Event *event) 4709983Sstever@gmail.com{ 4719983Sstever@gmail.com async_queue_mutex->lock(); 4729983Sstever@gmail.com async_queue.push_back(event); 4739983Sstever@gmail.com async_queue_mutex->unlock(); 4749983Sstever@gmail.com} 4759983Sstever@gmail.com 4769983Sstever@gmail.comvoid 4779983Sstever@gmail.comEventQueue::handleAsyncInsertions() 4789983Sstever@gmail.com{ 4799983Sstever@gmail.com assert(this == curEventQueue()); 4809983Sstever@gmail.com async_queue_mutex->lock(); 4819983Sstever@gmail.com 4829983Sstever@gmail.com while (!async_queue.empty()) { 4839983Sstever@gmail.com insert(async_queue.front()); 4849983Sstever@gmail.com async_queue.pop_front(); 4859983Sstever@gmail.com } 4869983Sstever@gmail.com 4879983Sstever@gmail.com async_queue_mutex->unlock(); 4889983Sstever@gmail.com} 489