DIRECTORY AMEvents USING [Debugged, Debugging], Process USING [GetCurrent, MsecToTicks, Pause], DebuggerSwap USING [CallDebugger], ViewerClasses USING [Column, Lock, Viewer], ViewerDebug USING [debugging], ViewerLocks USING [], ViewerGroupLocks USING [], ViewerOps USING [BlinkDisplay, EnumerateChildren, EnumerateViewers, EnumProc], ViewerPrivate; ViewerLocksImpl: CEDAR MONITOR IMPORTS AMEvents, Process, DebuggerSwap, ViewerOps EXPORTS ViewerLocks, ViewerGroupLocks, ViewerPrivate = BEGIN CARD: TYPE = LONG CARDINAL; Column: TYPE = ViewerClasses.Column; Lock: TYPE = ViewerClasses.Lock; Viewer: TYPE = ViewerClasses.Viewer; lockFree: CONDITION; readLocksExclusive: BOOL _ FALSE; treeOwner: PROCESS _ NIL; treeUsers: CARDINAL _ 0; ColumnInfo: TYPE = RECORD [lock: Lock, wedgeCount: CARDINAL]; columns: ARRAY Column OF ColumnInfo _ ALL[[lock: [process: NIL, count: 0], wedgeCount: 0]]; CallRootUnderWriteLock: PUBLIC PROC [proc: PROC, viewer: Viewer] = { CallUnderWriteLock[proc, viewer]; }; CallRootAndLinksUnderWriteLock: PUBLIC PROC [proc: PROC, viewer: Viewer] = { WHILE viewer # NIL AND NOT viewer.destroyed DO root: Viewer _ GetRoot[viewer]; link1: Viewer _ IF root = NIL THEN NIL ELSE root.link; link2: Viewer _ IF link1 = NIL THEN NIL ELSE link1.link; quick: BOOL _ TRUE; inner: PROC = { IF root # GetRoot[viewer] THEN RETURN; IF root.link # link1 THEN RETURN; IF quick AND (link1 # NIL AND link2 # root) THEN RETURN; proc[]; win _ TRUE; }; win: BOOL _ FALSE; SELECT TRUE FROM root = viewer AND link1 = NIL => CallUnderWriteLock[inner, viewer]; link1 = NIL OR link2 = root => CallUnderWriteLocks[inner, viewer, root, link1]; ENDCASE => {quick _ FALSE; CallUnderViewerTreeLock[inner]}; IF win THEN RETURN; ENDLOOP; }; LockViewerTree: PUBLIC ENTRY PROC = { ENABLE UNWIND => NULL; process: PROCESS = CurrentProcess[]; DO IF treeOwner = process THEN EXIT; FOR c: Column IN Column DO IF columns[c].wedgeCount # 0 THEN RETURN WITH ERROR Wedged[c]; ENDLOOP; IF treeUsers = 0 THEN { IF treeOwner # NIL THEN Error[]; EXIT; }; WAIT lockFree; ENDLOOP; treeUsers _ treeUsers + 1; treeOwner _ process; }; ReleaseViewerTree: PUBLIC ENTRY PROC = { ENABLE UNWIND => NULL; IF CurrentProcess[] # treeOwner THEN Error[]; IF treeUsers = 0 THEN Error[]; treeOwner _ NIL; treeUsers _ treeUsers - 1; BROADCAST lockFree; }; CallUnderViewerTreeLock: PUBLIC PROC [proc: PROC] = { LockViewerTree[]; proc[! UNWIND => ReleaseViewerTree[]]; ReleaseViewerTree[]; }; Wedged: PUBLIC SAFE ERROR [wedged: Column] = CODE; CallUnderColumnLock: PUBLIC PROC [proc: PROC, column: Column] = { ENABLE { AMEvents.Debugging => MarkColumnWedged[column]; AMEvents.Debugged => MarkColumnUnWedged[column]}; Wait: ENTRY PROC = {ENABLE UNWIND => NULL; WAIT lockFree}; AcquireColumnWriteLock[column ! Wedged => {Wait[]; RETRY}]; proc[! UNWIND => ReleaseColumnWriteLock[column]]; ReleaseColumnWriteLock[column]; }; CallUnderColumnLocks: PUBLIC PROC [proc: PROC, c0, c1: Column] = { ENABLE { AMEvents.Debugging => {MarkColumnWedged[c0]; MarkColumnWedged[c1]}; AMEvents.Debugged => {MarkColumnUnWedged[c0]; MarkColumnUnWedged[c1]}; }; Wait: ENTRY PROC = {ENABLE UNWIND => NULL; WAIT lockFree}; IF c0 > c1 THEN {c: Column _ c0; c0 _ c1; c1 _ c}; AcquireColumnWriteLock[c0 ! Wedged => {Wait[]; RETRY}]; AcquireColumnWriteLock[c1 ! Wedged => {Wait[]; RETRY}]; proc[! UNWIND => {ReleaseColumnWriteLock[c1]; ReleaseColumnWriteLock[c0]}]; ReleaseColumnWriteLock[c1]; ReleaseColumnWriteLock[c0]; }; AcquireColumnWriteLock: PUBLIC ENTRY PROC [column: Column] = { ENABLE UNWIND => NULL; IF ~ColumnWriteLockInternal[column] THEN RETURN WITH ERROR Wedged[column]; }; ColumnWriteLockInternal: INTERNAL PROC [column: Column] RETURNS [success: BOOL _ TRUE] = { process: PROCESS ~ CurrentProcess[]; count: CARDINAL _ 0; DO IF columns[column].wedgeCount>0 THEN RETURN [FALSE]; count _ columns[column].lock.count; IF count=0 OR columns[column].lock.process = process THEN IF treeOwner = process OR treeOwner = NIL THEN EXIT; WAIT lockFree; ENDLOOP; treeUsers _ treeUsers + 1; columns[column].lock _ [process, count+1]; }; ReleaseColumnWriteLock: PUBLIC ENTRY PROC [column: Column] = { ENABLE UNWIND => NULL; ReleaseColumnLockInternal[column]; }; ReleaseColumnLockInternal: INTERNAL PROC [column: Column] = { IF columns[column].lock.count = 0 THEN Error[]; IF treeUsers = 0 THEN Error[]; treeUsers _ treeUsers - 1; IF treeUsers = 0 THEN { treeOwner _ NIL; BROADCAST lockFree; }; IF (columns[column].lock.count _ columns[column].lock.count-1) = 0 THEN { columns[column].lock.process _ NIL; BROADCAST lockFree; }; }; ReadLockColumn: INTERNAL PROC [viewer: Viewer _ NIL, column: Column _ static] RETURNS [success: BOOL _ TRUE] = { process: PROCESS ~ CurrentProcess[]; DO IF viewer # NIL THEN column _ ViewerColumn[viewer]; IF columns[column].wedgeCount>0 THEN RETURN [FALSE]; SELECT columns[column].lock.process FROM NIL, process => IF treeOwner = process OR treeOwner = NIL THEN EXIT; ENDCASE; WAIT lockFree; ENDLOOP; treeUsers _ treeUsers + 1; columns[column].lock.count _ columns[column].lock.count+1; }; ColumnWedged: PUBLIC ENTRY PROC [column: Column] RETURNS [wedged, write: BOOL] = TRUSTED { ENABLE UNWIND => NULL; RETURN [columns[column].wedgeCount # 0, columns[column].lock.process # NIL]; }; MarkViewerWedged: PUBLIC PROC [viewer: Viewer] = { ENABLE UNWIND => NULL; IF viewer # NIL THEN { viewer.paintingWedged _ TRUE; MarkColumnUnWedged[ViewerColumn[viewer]]; }; }; MarkViewerUnWedged: PUBLIC PROC [viewer: Viewer] = { ENABLE UNWIND => NULL; IF viewer # NIL THEN { viewer.paintingWedged _ FALSE; MarkColumnUnWedged[ViewerColumn[viewer]]; }; }; MarkColumnWedged: PUBLIC ENTRY PROC [column: Column] = { ENABLE UNWIND => NULL; IF ViewerDebug.debugging THEN RETURN; IF columns[column].wedgeCount = 0 THEN THROUGH [0..2) DO ViewerOps.BlinkDisplay[]; Process.Pause[Process.MsecToTicks[200]]; ENDLOOP; columns[column].wedgeCount _ columns[column].wedgeCount+1; }; MarkColumnUnWedged: PUBLIC ENTRY PROC [column: Column] = { ENABLE UNWIND => NULL; IF ViewerDebug.debugging THEN RETURN; IF (columns[column].wedgeCount _ columns[column].wedgeCount-1) = 0 THEN BROADCAST lockFree; }; LockOrder: PUBLIC PROC [v0, v1: Viewer] RETURNS [BOOL] = { SELECT TRUE FROM v0 = NIL => GO TO swapped; v1 = NIL => {}; ENDCASE => { c0: Column _ IF v0.iconic THEN static ELSE v0.column; c1: Column _ IF v1.iconic THEN static ELSE v1.column; SELECT TRUE FROM c0 > c1 => {}; c0 < c1 => GO TO swapped; LOOPHOLE[v0, CARD] < LOOPHOLE[v1, CARD] => GO TO swapped; ENDCASE; }; RETURN [TRUE]; EXITS swapped => RETURN [FALSE]; }; CallUnderWriteLock: PUBLIC PROC [proc: PROC, viewer: Viewer] = { IF viewer # NIL AND NOT viewer.destroyed THEN DO root: Viewer _ GetRoot[viewer]; AcquireWriteLock[root]; IF root # GetRoot[viewer] THEN {ReleaseWriteLock[root]; LOOP}; proc[ ! UNWIND => ReleaseWriteLock[root]; AMEvents.Debugging => MarkViewerWedged[root]; AMEvents.Debugged => MarkViewerUnWedged[root]; ]; ReleaseWriteLock[root]; RETURN; ENDLOOP; }; CallUnderWriteLocks: PUBLIC PROC [proc: PROC, v0, v1, v2: Viewer _ NIL] = { DO r0: Viewer _ GetRoot[v0]; r1: Viewer _ GetRoot[v1]; r2: Viewer _ GetRoot[v2]; AcquireWriteLocks[r0, r1, r2]; IF r0 = GetRoot[v0] AND r1 = GetRoot[v1] AND r2 = GetRoot[v2] THEN { proc[ ! UNWIND => ReleaseWriteLocks[r0, r1, r2]; AMEvents.Debugging => { IF r0 # NIL THEN MarkViewerWedged[r0]; IF r1 # NIL THEN MarkViewerWedged[r1]; IF r2 # NIL THEN MarkViewerWedged[r2]}; AMEvents.Debugged => { IF r0 # NIL THEN MarkViewerUnWedged[r0]; IF r1 # NIL THEN MarkViewerUnWedged[r1]; IF r2 # NIL THEN MarkViewerUnWedged[r2]}; ]; ReleaseWriteLocks[r0, r1, r2]; RETURN; }; ReleaseWriteLocks[r0, r1, r2]; ENDLOOP; }; CallUnderReadLock: PUBLIC PROC [proc: PROC, viewer: Viewer] = { IF viewer # NIL AND NOT viewer.destroyed THEN DO root: Viewer _ GetRoot[viewer]; AcquireReadLock[root]; proc[ ! UNWIND => ReleaseReadLock[root]; AMEvents.Debugging => MarkViewerWedged[root]; AMEvents.Debugged => MarkViewerUnWedged[root]; ]; ReleaseReadLock[root]; ENDLOOP; }; CallUnderReadLocks: PUBLIC PROC [proc: PROC, v0, v1, v2: Viewer _ NIL] = { DO r0: Viewer _ GetRoot[v0]; r1: Viewer _ GetRoot[v1]; r2: Viewer _ GetRoot[v2]; AcquireReadLocks[r0, r1, r2]; IF r0 = GetRoot[v0] AND r1 = GetRoot[v1] AND r2 = GetRoot[v2] THEN { proc[ ! UNWIND => ReleaseReadLocks[r0, r1, r2]; AMEvents.Debugging => { IF r0 # NIL THEN MarkViewerWedged[r0]; IF r1 # NIL THEN MarkViewerWedged[r1]; IF r2 # NIL THEN MarkViewerWedged[r2]}; AMEvents.Debugged => { IF r0 # NIL THEN MarkViewerUnWedged[r0]; IF r1 # NIL THEN MarkViewerUnWedged[r1]; IF r2 # NIL THEN MarkViewerUnWedged[r2]}; ]; ReleaseReadLocks[r0, r1, r2]; RETURN; }; ReleaseReadLocks[r0, r1, r2]; ENDLOOP; }; AcquireWriteLock: PUBLIC ENTRY PROC [viewer: Viewer] = { ENABLE UNWIND => NULL; IF NOT ReadLockColumn[viewer] THEN RETURN WITH ERROR Wedged[ViewerColumn[viewer]]; WriteLockInternal[viewer]; }; WriteLockInternal: INTERNAL PROC [viewer: Viewer] = { IF viewer # NIL THEN { process: PROCESS ~ CurrentProcess[]; UNTIL (viewer.lock.count=0 OR viewer.lock.process=process) DO WAIT lockFree; ENDLOOP; viewer.lock _ [process, viewer.lock.count+1]; }; }; ReleaseWriteLock: PUBLIC ENTRY PROC [viewer: Viewer] = { ENABLE UNWIND => NULL; IF viewer # NIL THEN ReleaseLockInternal[viewer]; }; AcquireWriteLocks: PUBLIC ENTRY PROC [v0, v1, v2: Viewer _ NIL] = { ENABLE UNWIND => NULL; c: Column; { [v0, v1, v2] _ LockColumnsForViewers[v0, v1, v2 ! Wedged => {c _ wedged; GO TO wedgedError} ]; EXITS wedgedError => RETURN WITH ERROR Wedged[c]; }; IF v0 # NIL THEN WriteLockInternal[v0]; IF v1 # NIL THEN WriteLockInternal[v1]; IF v2 # NIL THEN WriteLockInternal[v2]; }; ReleaseWriteLocks: PUBLIC ENTRY PROC [v0, v1, v2: Viewer _ NIL] = { ENABLE UNWIND => NULL; IF v0 # NIL THEN ReleaseLockInternal[v0]; IF v1 # NIL THEN ReleaseLockInternal[v1]; IF v2 # NIL THEN ReleaseLockInternal[v2]; }; AcquireReadLock: PUBLIC ENTRY PROC [viewer: Viewer] = { ENABLE UNWIND => NULL; IF NOT ReadLockColumn[viewer] THEN RETURN WITH ERROR Wedged[ViewerColumn[viewer]]; ReadLockInternal[viewer]; }; ReadLockInternal: INTERNAL PROC [viewer: Viewer] = { process: PROCESS = CurrentProcess[]; UNTIL (viewer.lock.process=NIL OR viewer.lock.process=process) DO WAIT lockFree; ENDLOOP; IF readLocksExclusive THEN viewer.lock.process _ process; viewer.lock.count _ viewer.lock.count+1; }; ReleaseReadLock: PUBLIC ENTRY PROC [viewer: Viewer] = { ENABLE UNWIND => NULL; IF viewer # NIL THEN ReleaseLockInternal[viewer]; }; AcquireReadLocks: PUBLIC ENTRY PROC [v0, v1, v2: Viewer _ NIL] = { ENABLE UNWIND => NULL; c: Column; { [v0, v1, v2] _ LockColumnsForViewers[v0, v1, v2 ! Wedged => {c _ wedged; GO TO wedgedError} ]; EXITS wedgedError => RETURN WITH ERROR Wedged[c]; }; IF v0 # NIL THEN ReadLockInternal[v0]; IF v1 # NIL THEN ReadLockInternal[v1]; IF v2 # NIL THEN ReadLockInternal[v2]; }; ReleaseReadLocks: PUBLIC ENTRY PROC [v0, v1, v2: Viewer _ NIL] = { ENABLE UNWIND => NULL; IF v0 # NIL THEN ReleaseLockInternal[v0]; IF v1 # NIL THEN ReleaseLockInternal[v1]; IF v2 # NIL THEN ReleaseLockInternal[v2]; }; ReleaseLockInternal: INTERNAL PROC [viewer: Viewer] = { ReleaseColumnLockInternal[ViewerColumn[viewer]]; IF viewer.lock.count = 0 THEN Error[]; IF (viewer.lock.count _ viewer.lock.count-1) = 0 THEN { viewer.lock.process _ NIL; BROADCAST lockFree; }; }; Error: PROC = { DebuggerSwap.CallDebugger["ViewerLocks bug"L]; }; GetRoot: ENTRY PROC [viewer: Viewer] RETURNS [root: Viewer] = { root _ viewer; IF root # NIL THEN DO next: Viewer _ root.parent; IF next = NIL THEN EXIT; root _ next; ENDLOOP; }; SetReadExclusive: PROC [state: BOOL _ TRUE] = { LockViewerTree[]; readLocksExclusive _ state; ReleaseViewerTree[]; }; ClearAllLocks: ENTRY PROC = { ClearLock: ViewerOps.EnumProc = { IF v.parent = NIL THEN ViewerOps.EnumerateChildren[v, ClearLock]; v.lock _ [NIL, 0]; }; FOR c: Column IN Column DO columns[c] _ [[NIL, 0], 0]; ENDLOOP; ViewerOps.EnumerateViewers[ClearLock]; treeOwner _ NIL; treeUsers _ 0; BROADCAST lockFree; }; LockColumnsForViewers: INTERNAL PROC [v0, v1, v2: Viewer] RETURNS [Viewer, Viewer, Viewer] = { DO c0, c1, c2: Column _ static; b0, b1, b2: BOOL _ FALSE; FreeAllColumns: INTERNAL PROC = { IF b0 THEN ReleaseColumnLockInternal[c0]; IF b1 THEN ReleaseColumnLockInternal[c1]; IF b2 THEN ReleaseColumnLockInternal[c2]; }; [v0, v1] _ SortViewers2[v0, v1]; [v1, v2] _ SortViewers2[v1, v2]; [v0, v1] _ SortViewers2[v0, v1]; IF v0 = NIL THEN EXIT; b0 _ ReadLockColumn[column: c0 _ ViewerColumn[v0]]; IF ~b0 THEN {FreeAllColumns[]; ERROR Wedged[c0]}; IF c0 # ViewerColumn[v0] THEN {FreeAllColumns[]; LOOP}; IF v1 = NIL THEN EXIT; b1 _ ReadLockColumn[column: c1 _ ViewerColumn[v1]]; IF ~b1 THEN {FreeAllColumns[]; ERROR Wedged[c1]}; IF c1 # ViewerColumn[v1] THEN {FreeAllColumns[]; LOOP}; IF v2 = NIL THEN EXIT; b2 _ ReadLockColumn[column: c2 _ ViewerColumn[v2]]; IF ~b2 THEN {FreeAllColumns[]; ERROR Wedged[c2]}; IF c2 # ViewerColumn[v2] THEN {FreeAllColumns[]; LOOP}; EXIT; ENDLOOP; RETURN [v0, v1, v2]; }; SortViewers2: PROC [v0, v1: Viewer] RETURNS [Viewer, Viewer] = INLINE { SELECT TRUE FROM v0 = NIL => GO TO swapped; v1 = NIL => {}; ENDCASE => { c0: Column _ IF v0.iconic THEN static ELSE v0.column; c1: Column _ IF v1.iconic THEN static ELSE v1.column; SELECT TRUE FROM c0 > c1 => {}; c0 < c1 => GO TO swapped; LOOPHOLE[v0, CARD] < LOOPHOLE[v1, CARD] => GO TO swapped; ENDCASE; }; RETURN [v0, v1]; EXITS swapped => RETURN [v1, v0]; }; ViewerColumn: PROC [viewer: Viewer] RETURNS [column: Column] = INLINE { RETURN [IF viewer.iconic THEN static ELSE viewer.column]; }; CurrentProcess: PROC RETURNS [PROCESS] = TRUSTED INLINE { RETURN [LOOPHOLE[Process.GetCurrent[]]]; }; END. ViewerLocksImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Russ Atkinson (RRA) June 11, 1985 11:47:54 am PDT Doug Wyatt, April 15, 1985 0:08:27 am PST Rationale There are 3 tiers of locks: tree, column, and viewer. Tree: write lock gives exclusive access to the whole viewer tree read lock prevents tree write locks at least a read lock is required for column access Column: write lock gives exclusive access to the column read lock prevents write locks on the column at least a read lock is required for viewer access Viewer: write lock gives exclusive access to the viewer read lock prevents write locks (currently the readLocksExclusive flag causes read locks to be exclusive) Write locks are owned by processes. Multiple requests by a process for a lock it already owns will be granted. Possession of a read lock without a write lock prevents acquiring a write lock. Acquisition order: Locks must be acquired first by tree, then by column, then by viewer. Within a tier, locks are acquired by column number or viewer address. Locks may be released in any order, but once a lock is released, no more should be acquired! Types Global variables BROADCAST when any lock is released If TRUE, then read locks on viewers will be exclusive access to the locking process. This is a hack for debugging purposes right now. The current process holding onto the whole tree The current # of users of the tree (should be the sum of the column locks) Root viewer operations Note: no longer different than CallUnderWriteLock First check to see if the viewer structure is the same as when we started Tree lock tier This entry acquires a write lock on the tree to get exclusive access to all columns and viewers. If we already own the lock then we exit quickly. At each iteration, check for any column being wedged. This entry releases the write lock on the tree. Column lock tier Get a read lock on the tree and a write lock on the column. Don't exit unless we can get both of them simultaneously. The column is wedged, so we can't get acquire the lock. We can have the column write lock We can have the tree read lock, too. When we can't get the tree read lock OR the column write lock we wait for something to happen and retry. So far we have not made any global changes. Finally bump the counts on the locks for the tree and column First, test for error conditions and crash if they show up. Now release the tree read lock that we got for this column Now release the column read|write lock Get a read lock on the column and the tree. Don't exit unless we can get both of them simultaneously. The column is wedged, so we can't get acquire the lock. We can have the read lock We can have the tree lock, too. When we can't get the tree read lock OR the column read lock we wait for something to happen and retry. So far we have not made any global changes. Finally bump the counts on the locks for the tree and column Viewer lock tier Locks the given viewer, assuming that the columns are already locked. No errors should occur in this routine. Viewers must be sorted so that they are locked from highest to lowest, by column and by address. We also need to lock the columns BEFORE locking the viewers themselves, since we can otherwise get deadlock for viewers in multiple columns. no sort required Acquires the read lock for the viewer, assuming that the column lock is already acquired RRA: note this test. It is a debugging aid to determine if it prevents painting bugs. viewers must be sorted so that they are locked from highest to lowest. As with write locks, we need to acquire the columns first to prevent deadlocks with multiple columns no sort required Utilities Safely change the state of readLocksExclusive. This is a desperately unsafe operation! Given three viewers, first sort them by column and address order. Then try to acquire the column lock(s) for those viewers in that order. There are two ways that this will fail: 1. If any column in the viewers is wedged, then release all of the column locks acquired, and raise ERROR Wedged[column] for the wedged column. 2. If any viewer column changes before all of the locks are acquired, then release all of the acquired column locks and try again. Since column changes are rare, this should eventually succeed. The viewers are returned in sorted order so that subsequent read or write locks can be acquired in the proper (non-deadlock) order. This relies on everyone acquiring locks in the same order. Now the viewers are in column order (and address order). The column order should not change while we are waiting, or there will be trouble! For now, we check this to make sure that it stays the same, and loop if it changes. This is a helpful routine for LockColumnsForViewers. It is INLINE to save on procedure calls (is this necessary?), and should have the same sort order as LockOrder. Κζ– "Cedar" style˜codešœ™Kšœ Οmœ1™Kšžœ˜—šžœžœ˜Kšžœ žœžœ ˜ Kšžœ˜Kšœ˜—Kšžœ ˜Kšžœ˜—Kšœ˜Kšœ˜Kšœ˜—K˜š œžœžœžœ˜(Kšœ/™/Kšžœžœžœ˜Kšžœžœ ˜-Kšžœžœ ˜Kšœ žœ˜Kšœ˜Kšž œ ˜Kšœ˜K˜—š œžœžœžœ˜5Kšœ˜Kšœžœ˜&Kšœ˜Kšœ˜——™š œžœžœžœžœ˜2K˜—š œžœžœžœ˜Ašžœ˜ Kšœ/˜/Kšœ1˜1—Kš œžœžœžœžœžœžœ ˜:Kšœ3žœ˜;Kšœžœ$˜1Kšœ˜Kšœ˜K˜—š œžœžœžœ˜Bšžœ˜ KšœC˜CKšœF˜FKšœ˜—Kš œžœžœžœžœžœžœ ˜:Kšžœ žœ#˜2Kšœ/žœ˜7Kšœ/žœ˜7Kšœžœ>˜KKšœ˜Kšœ˜Kšœ˜K˜—š œžœžœžœ˜>Kšžœžœžœ˜Kš žœ"žœžœžœžœ˜JKšœ˜K˜—š  œžœžœžœ žœžœ˜ZKšœ žœ˜$Kšœžœ˜K˜Kšœv™všž˜šžœž˜$Kšœ7™7Kšžœžœ˜—Kšœ#˜#šžœ žœ(ž˜9Kšœ!™!š žœžœ žœžœžœ˜4Kšœ$™$——šžœ ˜Kšœ•™•—Kšžœ˜K˜—Kšœ<™Kšžœžœžœ˜Kšœ"˜"Kšœ˜K˜—š œžœžœ˜=Kšœ;™;Kšžœ žœ ˜/Kšžœžœ ˜K˜Kšœ:™:Kšœ˜šžœžœ˜Kšœ žœ˜Kšž œ ˜K˜K˜—Kšœ&™&šžœAžœ˜IKšœžœ˜#Kšž œ ˜Kšœ˜—Kšœ˜K˜—š œžœžœžœžœ žœžœ˜pKšœ žœ˜$K˜Kšœf™fšž˜Kšžœ žœžœ˜3šžœž˜$Kšœ7™7Kšžœžœ˜—šžœž˜(šžœ ˜Kšœ™š žœžœ žœžœžœ˜4Kšœ™——Kšžœ˜—šžœ ˜Kšœ”™”—Kšžœ˜K˜—Kšœ<™šœ˜šœ˜Kšžœ˜!Kšœ-˜-Kšœ.˜.—Kšœ˜—Kšœ˜Kšžœ˜Kšžœ˜—Kšœ˜K˜—š  œžœžœžœžœ˜Kšž˜K˜K˜K˜Kšœ˜šžœžœžœžœ˜Dšœ˜šœ˜Kšžœ"˜(šœ˜Kšžœžœžœ˜&Kšžœžœžœ˜&Kšžœžœžœ˜'—šœ˜Kšžœžœžœ˜(Kšžœžœžœ˜(Kšžœžœžœ˜)——Kšœ˜—Kšœ˜Kšžœ˜K˜—Kšœ˜Kšžœ˜—Kšœ˜K˜—š œžœžœžœ˜?š žœ žœžœžœžœž˜0Kšœ˜Kšœ˜šœ˜šœ˜Kšžœ˜ Kšœ-˜-Kšœ.˜.—Kšœ˜—Kšœ˜Kšžœ˜—Kšœ˜K˜—š  œžœžœžœžœ˜Jšž˜K˜K˜K˜Kšœ˜šžœžœžœžœ˜Dšœ˜šœ˜Kšžœ!˜'šœ˜Kšžœžœžœ˜&Kšžœžœžœ˜&Kšžœžœžœ˜'—šœ˜Kšžœžœžœ˜(Kšžœžœžœ˜(Kšžœžœžœ˜)——Kšœ˜—Kšœ˜Kšžœ˜K˜—Kšœ˜Kšžœ˜—Kšœ˜K˜—š œžœžœžœ˜8Kšžœžœžœ˜šžœžœž˜"Kšžœžœžœ˜/—Kšœ˜Kšœ˜K˜—š œžœžœ˜5Kšœn™nšžœ žœžœ˜Kšœ žœ˜$Kš žœžœžœžœ žœ˜UKšœ-˜-K˜—Kšœ˜K˜—š œžœžœžœ˜8Kšžœžœžœ˜Kšžœ žœžœ˜1Kšœ˜K˜—š  œžœžœžœžœ˜CKšœƒžœe™ξKšžœžœžœ˜K˜ ˜šœ/˜/Kšœžœžœ ˜+Kšœ˜—Kšžœžœžœžœ ˜1K˜—Kšžœžœžœ˜'Kšžœžœžœ˜'Kšžœžœžœ˜'Kšœ˜K˜—š  œžœžœžœžœ˜CKšœ™Kšžœžœžœ˜Kšžœžœžœ˜)Kšžœžœžœ˜)Kšžœžœžœ˜)Kšœ˜K˜—š œžœžœžœ˜7Kšžœžœžœ˜šžœžœž˜"Kšžœžœžœ˜/—Kšœ˜Kšœ˜K˜—š œžœžœ˜4KšœX™XKšœ žœ˜$Kš žœžœžœžœžœ žœ˜Yšžœžœ˜9KšžœS™V—Kšœ(˜(Kšœ˜K˜—š œžœžœžœ˜7Kšžœžœžœ˜Kšžœ žœžœ˜1Kšœ˜K˜—š  œžœžœžœžœ˜BKšœ¬™¬Kšžœžœžœ˜K˜ ˜šœ/˜/Kšœžœžœ ˜+Kšœ˜—Kšžœžœžœžœ ˜1K˜—Kšžœžœžœ˜&Kšžœžœžœ˜&Kšžœžœžœ˜&Kšœ˜K˜—š  œžœžœžœžœ˜BKšœ™Kšžœžœžœ˜Kšžœžœžœ˜)Kšžœžœžœ˜)Kšžœžœžœ˜)Kšœ˜K˜—š œžœžœ˜7Kšœ0˜0Kšžœžœ ˜&šžœ/žœ˜7Kšœžœ˜Kšž œ ˜Kšœ˜—Kšœ˜——™ š œžœ˜Kšœ.˜.Kšœ˜K˜—š œž œžœ˜?K˜šžœžœžœž˜K˜Kšžœžœžœžœ˜K˜ Kšžœ˜—K˜K˜—š œžœ žœžœ˜/Kšœ.™.Kšœ˜Kšœ˜Kšœ˜K˜—K˜š  œžœžœ˜Kšœ'™'šœ!˜!Kšžœ žœžœ+˜AKšœ žœ˜Kšœ˜—Kš žœ žœžœžœ žœ˜?K˜&Kšœ žœ˜Kšœ˜Kšž œ ˜Kšœ˜K˜—š œžœžœžœ˜^šœ³™³Kšœ™K™Γ—K™Ώšž˜K˜Kšœ žœžœ˜šœžœžœ˜!Kšžœžœ˜)Kšžœžœ˜)Kšžœžœ˜)K˜—Kšœ ˜ Kšœ ˜ Kšœ ˜ K˜Kšœα™αK˜Kšžœžœžœžœ˜Kšœ3˜3Kšžœžœžœ ˜1Kšžœžœžœ˜7K˜Kšžœžœžœžœ˜Kšœ3˜3Kšžœžœžœ ˜1Kšžœžœžœ˜7K˜Kšžœžœžœžœ˜Kšœ3˜3Kšžœžœžœ ˜1Kšžœžœžœ˜7K˜Kšžœ˜Kšžœ˜—Kšžœ˜K˜K˜—š  œžœžœžœ˜GKšœ<žœc™₯šžœžœž˜Kšœžœžœžœ ˜Kšœžœ˜šžœ˜ Kšœ žœ žœžœ ˜5Kšœ žœ žœžœ ˜5šžœžœž˜Kšœ˜Kšœ žœžœ ˜Kš žœžœžœžœžœžœ ˜9Kšžœ˜—K˜——Kšžœ ˜Kšžœ žœ ˜!K˜K˜—š  œžœžœžœ˜GKšžœžœžœžœ˜9Kšœ˜K˜—š  œžœžœžœžœžœ˜9Kšžœžœ˜(K˜——K˜Kšžœ˜—…—5n`r