15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)/* 25c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. 35c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * 45c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Redistribution and use in source and binary forms, with or without 55c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * modification, are permitted provided that the following conditions 65c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * are met: 75c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * 1. Redistributions of source code must retain the above copyright 85c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * notice, this list of conditions and the following disclaimer. 95c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * 2. Redistributions in binary form must reproduce the above copyright 105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * notice, this list of conditions and the following disclaimer in the 115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * documentation and/or other materials provided with the distribution. 125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * 135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 2302772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) */ 255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#ifndef Timer_h 275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#define Timer_h 285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 291e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)#include "platform/PlatformExport.h" 30d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)#include "platform/TraceLocation.h" 31f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)#include "platform/heap/Handle.h" 327757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch#include "wtf/Noncopyable.h" 337757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch#include "wtf/Threading.h" 347757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch#include "wtf/Vector.h" 355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 36c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)namespace blink { 375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// Time intervals are all in seconds. 395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 401e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)class PLATFORM_EXPORT TimerBase { 415d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles) WTF_MAKE_NONCOPYABLE(TimerBase); WTF_MAKE_FAST_ALLOCATED_WILL_BE_REMOVED; 425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)public: 435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) TimerBase(); 445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) virtual ~TimerBase(); 455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 46d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) void start(double nextFireInterval, double repeatInterval, const TraceLocation&); 475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 48d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) void startRepeating(double repeatInterval, const TraceLocation& caller) 49d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) { 50d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) start(repeatInterval, repeatInterval, caller); 51d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) } 52d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) void startOneShot(double interval, const TraceLocation& caller) 53d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) { 54d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) start(interval, 0, caller); 55d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) } 565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) void stop(); 585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) bool isActive() const; 59d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) const TraceLocation& location() const { return m_location; } 605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) double nextFireInterval() const; 625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) double nextUnalignedFireInterval() const; 635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) double repeatInterval() const { return m_repeatInterval; } 645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 6553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) void augmentRepeatInterval(double delta) { 6653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) setNextFireTime(m_nextFireTime + delta); 6753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) m_repeatInterval += delta; 6853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) } 695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) void didChangeAlignmentInterval(); 715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) static void fireTimersInNestedEventLoop(); 735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)private: 755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) virtual void fired() = 0; 765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) virtual double alignedFireTime(double fireTime) const { return fireTime; } 785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) void checkConsistency() const; 805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) void checkHeapIndex() const; 815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) void setNextFireTime(double); 835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) bool inHeap() const { return m_heapIndex != -1; } 855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 86926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) bool hasValidHeapPosition() const; 87926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) void updateHeapIfNeeded(double oldTime); 88926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) void heapDecreaseKey(); 905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) void heapDelete(); 915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) void heapDeleteMin(); 925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) void heapIncreaseKey(); 935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) void heapInsert(); 945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) void heapPop(); 955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) void heapPopMin(); 965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 97926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) Vector<TimerBase*>& timerHeap() const { ASSERT(m_cachedThreadGlobalTimerHeap); return *m_cachedThreadGlobalTimerHeap; } 98926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) double m_nextFireTime; // 0 if inactive 1005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) double m_unalignedNextFireTime; // m_nextFireTime not considering alignment interval 1015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) double m_repeatInterval; // 0 if not repeating 1025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) int m_heapIndex; // -1 if not in heap 1035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) unsigned m_heapInsertionOrder; // Used to keep order among equal-fire-time timers 104926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) Vector<TimerBase*>* m_cachedThreadGlobalTimerHeap; 105d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) TraceLocation m_location; 1065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 107197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch#if ENABLE(ASSERT) 1085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) ThreadIdentifier m_thread; 1095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#endif 1105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) friend class ThreadTimers; 1125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) friend class TimerHeapLessThanFunction; 1135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) friend class TimerHeapReference; 1145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}; 1155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1161e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)template <typename TimerFiredClass> 11709380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)class Timer FINAL : public TimerBase { 1185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)public: 1195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) typedef void (TimerFiredClass::*TimerFiredFunction)(Timer*); 1205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) Timer(TimerFiredClass* o, TimerFiredFunction f) 1225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) : m_object(o), m_function(f) { } 1235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)private: 12509380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) virtual void fired() OVERRIDE { (m_object->*m_function)(this); } 1265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1276f543c786fc42989f552b4daa774ca5ff32fa697Ben Murdoch // FIXME: oilpan: TimerBase should be moved to the heap and m_object should be traced. 1286f543c786fc42989f552b4daa774ca5ff32fa697Ben Murdoch // This raw pointer is safe as long as Timer<X> is held by the X itself (That's the case 1296f543c786fc42989f552b4daa774ca5ff32fa697Ben Murdoch // in the current code base). 130f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles) GC_PLUGIN_IGNORE("363031") 1315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) TimerFiredClass* m_object; 1325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) TimerFiredFunction m_function; 1335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}; 1345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)inline bool TimerBase::isActive() const 1365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 1375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) ASSERT(m_thread == currentThread()); 1385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return m_nextFireTime; 1395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 1405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1411e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)template <typename TimerFiredClass> 14209380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)class DeferrableOneShotTimer FINAL : private TimerBase { 1435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)public: 1445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) typedef void (TimerFiredClass::*TimerFiredFunction)(DeferrableOneShotTimer*); 1455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) DeferrableOneShotTimer(TimerFiredClass* o, TimerFiredFunction f, double delay) 1475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) : m_object(o) 1485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) , m_function(f) 1495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) , m_delay(delay) 1505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) , m_shouldRestartWhenTimerFires(false) 1515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) { 1525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 1535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 154d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) void restart(const TraceLocation& caller) 1555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) { 1565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Setting this boolean is much more efficient than calling startOneShot 1575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // again, which might result in rescheduling the system timer which 1585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // can be quite expensive. 1595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (isActive()) { 1615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_shouldRestartWhenTimerFires = true; 1625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return; 1635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 164d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) startOneShot(m_delay, caller); 1655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 1665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) using TimerBase::stop; 1685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) using TimerBase::isActive; 1691e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) 1705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)private: 17109380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) virtual void fired() OVERRIDE 1725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) { 1735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (m_shouldRestartWhenTimerFires) { 1745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_shouldRestartWhenTimerFires = false; 175d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) // FIXME: This should not be FROM_HERE. 176d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) startOneShot(m_delay, FROM_HERE); 1775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return; 1785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 1795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) (m_object->*m_function)(this); 1815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 1825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1836f543c786fc42989f552b4daa774ca5ff32fa697Ben Murdoch // FIXME: oilpan: TimerBase should be moved to the heap and m_object should be traced. 1846f543c786fc42989f552b4daa774ca5ff32fa697Ben Murdoch // This raw pointer is safe as long as Timer<X> is held by the X itself (That's the case 1856f543c786fc42989f552b4daa774ca5ff32fa697Ben Murdoch // in the current code base). 186f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles) GC_PLUGIN_IGNORE("363031") 1875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) TimerFiredClass* m_object; 1885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) TimerFiredFunction m_function; 1895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) double m_delay; 1915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) bool m_shouldRestartWhenTimerFires; 1925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}; 1935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 1955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#endif 197