1*8462SApril.Chin@Sun.COM#!/usr/bin/ksh93
2*8462SApril.Chin@Sun.COM
3*8462SApril.Chin@Sun.COM#
4*8462SApril.Chin@Sun.COM# CDDL HEADER START
5*8462SApril.Chin@Sun.COM#
6*8462SApril.Chin@Sun.COM# The contents of this file are subject to the terms of the
7*8462SApril.Chin@Sun.COM# Common Development and Distribution License (the "License").
8*8462SApril.Chin@Sun.COM# You may not use this file except in compliance with the License.
9*8462SApril.Chin@Sun.COM#
10*8462SApril.Chin@Sun.COM# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11*8462SApril.Chin@Sun.COM# or http://www.opensolaris.org/os/licensing.
12*8462SApril.Chin@Sun.COM# See the License for the specific language governing permissions
13*8462SApril.Chin@Sun.COM# and limitations under the License.
14*8462SApril.Chin@Sun.COM#
15*8462SApril.Chin@Sun.COM# When distributing Covered Code, include this CDDL HEADER in each
16*8462SApril.Chin@Sun.COM# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17*8462SApril.Chin@Sun.COM# If applicable, add the following below this CDDL HEADER, with the
18*8462SApril.Chin@Sun.COM# fields enclosed by brackets "[]" replaced with your own identifying
19*8462SApril.Chin@Sun.COM# information: Portions Copyright [yyyy] [name of copyright owner]
20*8462SApril.Chin@Sun.COM#
21*8462SApril.Chin@Sun.COM# CDDL HEADER END
22*8462SApril.Chin@Sun.COM#
23*8462SApril.Chin@Sun.COM
24*8462SApril.Chin@Sun.COM#
25*8462SApril.Chin@Sun.COM# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
26*8462SApril.Chin@Sun.COM# Use is subject to license terms.
27*8462SApril.Chin@Sun.COM#
28*8462SApril.Chin@Sun.COM
29*8462SApril.Chin@Sun.COM#
30*8462SApril.Chin@Sun.COM# filemutexdemo1 - a simple locking demo which supports read/write
31*8462SApril.Chin@Sun.COM# locks and critical sections (like JAVA's "syncronized" keyword)
32*8462SApril.Chin@Sun.COM#
33*8462SApril.Chin@Sun.COM
34*8462SApril.Chin@Sun.COM# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant
35*8462SApril.Chin@Sun.COMexport PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin
36*8462SApril.Chin@Sun.COM
37*8462SApril.Chin@Sun.COM# Make sure all math stuff runs in the "C" locale to avoid problems
38*8462SApril.Chin@Sun.COM# with alternative # radix point representations (e.g. ',' instead of
39*8462SApril.Chin@Sun.COM# '.' in de_DE.*-locales). This needs to be set _before_ any
40*8462SApril.Chin@Sun.COM# floating-point constants are defined in this script).
41*8462SApril.Chin@Sun.COMif [[ "${LC_ALL}" != "" ]] ; then
42*8462SApril.Chin@Sun.COM    export \
43*8462SApril.Chin@Sun.COM        LC_MONETARY="${LC_ALL}" \
44*8462SApril.Chin@Sun.COM        LC_MESSAGES="${LC_ALL}" \
45*8462SApril.Chin@Sun.COM        LC_COLLATE="${LC_ALL}" \
46*8462SApril.Chin@Sun.COM        LC_CTYPE="${LC_ALL}"
47*8462SApril.Chin@Sun.COM        unset LC_ALL
48*8462SApril.Chin@Sun.COMfi
49*8462SApril.Chin@Sun.COMexport LC_NUMERIC=C
50*8462SApril.Chin@Sun.COM
51*8462SApril.Chin@Sun.COM# Definition for a mutex which uses the filesystem for locking
52*8462SApril.Chin@Sun.COMtypeset -T filemutex_t=(
53*8462SApril.Chin@Sun.COM	typeset name
54*8462SApril.Chin@Sun.COM
55*8462SApril.Chin@Sun.COM	typeset lock_dirname
56*8462SApril.Chin@Sun.COM
57*8462SApril.Chin@Sun.COM	typeset locked_exclusive="false"
58*8462SApril.Chin@Sun.COM	typeset locked_shared="false"
59*8462SApril.Chin@Sun.COM
60*8462SApril.Chin@Sun.COM	# keep track of subshell level. The problem is that we do not know a
61*8462SApril.Chin@Sun.COM	# way to figure out whether someone calls "unlock" in a subshell and then
62*8462SApril.Chin@Sun.COM	# leaves the subshell and calls "unlock" again
63*8462SApril.Chin@Sun.COM	integer subshell=-1
64*8462SApril.Chin@Sun.COM
65*8462SApril.Chin@Sun.COM	typeset lock_dirname
66*8462SApril.Chin@Sun.COM
67*8462SApril.Chin@Sun.COM	# create a filemutex instance (including lock directory)
68*8462SApril.Chin@Sun.COM	function create
69*8462SApril.Chin@Sun.COM	{
70*8462SApril.Chin@Sun.COM		# make sure we return an error if the init didn't work
71*8462SApril.Chin@Sun.COM		set -o errexit
72*8462SApril.Chin@Sun.COM
73*8462SApril.Chin@Sun.COM		[[ "$1" == "" ]] && return 1
74*8462SApril.Chin@Sun.COM
75*8462SApril.Chin@Sun.COM		_.name="$1"
76*8462SApril.Chin@Sun.COM		_.lock_dirname="/tmp/filemutex_t_${_.name}.lock"
77*8462SApril.Chin@Sun.COM
78*8462SApril.Chin@Sun.COM		mkdir "${_.lock_dirname}"
79*8462SApril.Chin@Sun.COM
80*8462SApril.Chin@Sun.COM		# last entry, used to mark the mutex as initalised+valid
81*8462SApril.Chin@Sun.COM		(( _.subshell=.sh.subshell ))
82*8462SApril.Chin@Sun.COM		return 0
83*8462SApril.Chin@Sun.COM	}
84*8462SApril.Chin@Sun.COM
85*8462SApril.Chin@Sun.COM	# use a filemutex instance (same as "create" but without creating
86*8462SApril.Chin@Sun.COM	# the lock directory)
87*8462SApril.Chin@Sun.COM	function create_child
88*8462SApril.Chin@Sun.COM	{
89*8462SApril.Chin@Sun.COM		# make sure we return an error if the init didn't work
90*8462SApril.Chin@Sun.COM		set -o errexit
91*8462SApril.Chin@Sun.COM
92*8462SApril.Chin@Sun.COM		[[ "$1" == "" ]] && return 1
93*8462SApril.Chin@Sun.COM
94*8462SApril.Chin@Sun.COM		_.name="$1"
95*8462SApril.Chin@Sun.COM		_.lock_dirname="/tmp/filemutex_t_${_.name}.lock"
96*8462SApril.Chin@Sun.COM
97*8462SApril.Chin@Sun.COM		# last entry, used to mark the mutex as initalised+valid
98*8462SApril.Chin@Sun.COM		(( _.subshell=.sh.subshell ))
99*8462SApril.Chin@Sun.COM		return 0
100*8462SApril.Chin@Sun.COM	}
101*8462SApril.Chin@Sun.COM
102*8462SApril.Chin@Sun.COM	function check_subshell
103*8462SApril.Chin@Sun.COM	{
104*8462SApril.Chin@Sun.COM		(( _.subshell == .sh.subshell )) && return 0
105*8462SApril.Chin@Sun.COM		print -u2 -f "filemutex_t.%s(%s): Wrong subshell level\n" "$1" "${_.name}"
106*8462SApril.Chin@Sun.COM		return 1
107*8462SApril.Chin@Sun.COM	}
108*8462SApril.Chin@Sun.COM
109*8462SApril.Chin@Sun.COM	function try_lock_shared
110*8462SApril.Chin@Sun.COM	{
111*8462SApril.Chin@Sun.COM		_.check_subshell "try_lock_shared" || return 1
112*8462SApril.Chin@Sun.COM
113*8462SApril.Chin@Sun.COM		mkdir "${_.lock_dirname}/shared_${PPID}_$$" 2>/dev/null || return 1
114*8462SApril.Chin@Sun.COM		_.locked_shared="true"
115*8462SApril.Chin@Sun.COM		return 0
116*8462SApril.Chin@Sun.COM	}
117*8462SApril.Chin@Sun.COM
118*8462SApril.Chin@Sun.COM	function lock_shared
119*8462SApril.Chin@Sun.COM	{
120*8462SApril.Chin@Sun.COM		float interval=0.2
121*8462SApril.Chin@Sun.COM
122*8462SApril.Chin@Sun.COM		_.check_subshell "lock_shared" || return 1
123*8462SApril.Chin@Sun.COM
124*8462SApril.Chin@Sun.COM		while ! _.try_lock_shared ; do sleep ${interval} ; (( interval+=interval/10. )) ; done
125*8462SApril.Chin@Sun.COM		return 0
126*8462SApril.Chin@Sun.COM	}
127*8462SApril.Chin@Sun.COM
128*8462SApril.Chin@Sun.COM	function try_lock_exclusive
129*8462SApril.Chin@Sun.COM	{
130*8462SApril.Chin@Sun.COM		_.check_subshell "try_lock_exclusive" || return 1
131*8462SApril.Chin@Sun.COM
132*8462SApril.Chin@Sun.COM		rmdir "${_.lock_dirname}" 2>/dev/null || return 1
133*8462SApril.Chin@Sun.COM		_.locked_exclusive="true"
134*8462SApril.Chin@Sun.COM		return 0
135*8462SApril.Chin@Sun.COM	}
136*8462SApril.Chin@Sun.COM
137*8462SApril.Chin@Sun.COM	function lock_exclusive
138*8462SApril.Chin@Sun.COM	{
139*8462SApril.Chin@Sun.COM		float interval=0.2
140*8462SApril.Chin@Sun.COM
141*8462SApril.Chin@Sun.COM		_.check_subshell "lock_exclusive" || return 1
142*8462SApril.Chin@Sun.COM
143*8462SApril.Chin@Sun.COM		while ! _.try_lock_exclusive ; do sleep ${interval} ; (( interval+=interval/10. )) ; done
144*8462SApril.Chin@Sun.COM		return 0
145*8462SApril.Chin@Sun.COM	}
146*8462SApril.Chin@Sun.COM
147*8462SApril.Chin@Sun.COM	# critical section support (like java's "synchronized" keyword)
148*8462SApril.Chin@Sun.COM	function synchronized
149*8462SApril.Chin@Sun.COM	{
150*8462SApril.Chin@Sun.COM		integer retcode
151*8462SApril.Chin@Sun.COM
152*8462SApril.Chin@Sun.COM		_.check_subshell "synchronized" || return 1
153*8462SApril.Chin@Sun.COM
154*8462SApril.Chin@Sun.COM		_.lock_exclusive
155*8462SApril.Chin@Sun.COM
156*8462SApril.Chin@Sun.COM		"$@"
157*8462SApril.Chin@Sun.COM		(( retcode=$? ))
158*8462SApril.Chin@Sun.COM
159*8462SApril.Chin@Sun.COM		_.unlock
160*8462SApril.Chin@Sun.COM
161*8462SApril.Chin@Sun.COM		return ${retcode}
162*8462SApril.Chin@Sun.COM	}
163*8462SApril.Chin@Sun.COM
164*8462SApril.Chin@Sun.COM	# critical section support with shared lock
165*8462SApril.Chin@Sun.COM	function synchronized_shared
166*8462SApril.Chin@Sun.COM	{
167*8462SApril.Chin@Sun.COM		integer retcode
168*8462SApril.Chin@Sun.COM
169*8462SApril.Chin@Sun.COM		_.check_subshell "synchronized_shared" || return 1
170*8462SApril.Chin@Sun.COM
171*8462SApril.Chin@Sun.COM		_.lock_shared
172*8462SApril.Chin@Sun.COM
173*8462SApril.Chin@Sun.COM		"$@"
174*8462SApril.Chin@Sun.COM		(( retcode=$? ))
175*8462SApril.Chin@Sun.COM
176*8462SApril.Chin@Sun.COM		_.unlock
177*8462SApril.Chin@Sun.COM
178*8462SApril.Chin@Sun.COM		return ${retcode}
179*8462SApril.Chin@Sun.COM	}
180*8462SApril.Chin@Sun.COM
181*8462SApril.Chin@Sun.COM	function unlock
182*8462SApril.Chin@Sun.COM	{
183*8462SApril.Chin@Sun.COM		# return an error if rmdir/mkdir/check_subshell fail...
184*8462SApril.Chin@Sun.COM		set -o errexit
185*8462SApril.Chin@Sun.COM
186*8462SApril.Chin@Sun.COM		_.check_subshell "unlock"
187*8462SApril.Chin@Sun.COM
188*8462SApril.Chin@Sun.COM		if ${_.locked_shared} ; then
189*8462SApril.Chin@Sun.COM			rmdir "${_.lock_dirname}/shared_${PPID}_$$"
190*8462SApril.Chin@Sun.COM			_.locked_shared="false"
191*8462SApril.Chin@Sun.COM			return 0
192*8462SApril.Chin@Sun.COM		elif ${_.locked_exclusive} ; then
193*8462SApril.Chin@Sun.COM			mkdir "${_.lock_dirname}"
194*8462SApril.Chin@Sun.COM			_.locked_exclusive="false"
195*8462SApril.Chin@Sun.COM			return 0
196*8462SApril.Chin@Sun.COM		fi
197*8462SApril.Chin@Sun.COM
198*8462SApril.Chin@Sun.COM		print -u2 -f "filemutex_t.unlock(%s): mutex '%s' not locked." "$1" "${_.name}"
199*8462SApril.Chin@Sun.COM		return 1
200*8462SApril.Chin@Sun.COM	}
201*8462SApril.Chin@Sun.COM
202*8462SApril.Chin@Sun.COM	# destroy mutex if noone is using it anymore (not the same as "unset" !!))
203*8462SApril.Chin@Sun.COM	function destroy
204*8462SApril.Chin@Sun.COM	{
205*8462SApril.Chin@Sun.COM		_.check_subshell "destroy" || return 1
206*8462SApril.Chin@Sun.COM
207*8462SApril.Chin@Sun.COM		(${_.locked_exclusive} || ${_.locked_shared}) && _.unlock
208*8462SApril.Chin@Sun.COM		rmdir "${_.lock_dirname}"
209*8462SApril.Chin@Sun.COM		return 0
210*8462SApril.Chin@Sun.COM	}
211*8462SApril.Chin@Sun.COM)
212*8462SApril.Chin@Sun.COM
213*8462SApril.Chin@Sun.COM# main
214*8462SApril.Chin@Sun.COMbuiltin mkdir
215*8462SApril.Chin@Sun.COMbuiltin rmdir
216*8462SApril.Chin@Sun.COM
217*8462SApril.Chin@Sun.COMprint "## Start."
218*8462SApril.Chin@Sun.COM
219*8462SApril.Chin@Sun.COMtypeset -r mymutexname="hello_world"
220*8462SApril.Chin@Sun.COM
221*8462SApril.Chin@Sun.COMfilemutex_t fs
222*8462SApril.Chin@Sun.COM
223*8462SApril.Chin@Sun.COMfs.create "${mymutexname}" || print -u2 "Mutex init failed."
224*8462SApril.Chin@Sun.COM
225*8462SApril.Chin@Sun.COMprint "# Starting child which keeps an exclusive lock for 10 seconds..."
226*8462SApril.Chin@Sun.COM(
227*8462SApril.Chin@Sun.COM	filemutex_t child_fs
228*8462SApril.Chin@Sun.COM
229*8462SApril.Chin@Sun.COM	child_fs.create_child "${mymutexname}"
230*8462SApril.Chin@Sun.COM
231*8462SApril.Chin@Sun.COM	child_fs.lock_exclusive
232*8462SApril.Chin@Sun.COM	sleep 10
233*8462SApril.Chin@Sun.COM	child_fs.unlock
234*8462SApril.Chin@Sun.COM) &
235*8462SApril.Chin@Sun.COM
236*8462SApril.Chin@Sun.COMsleep 1
237*8462SApril.Chin@Sun.COM
238*8462SApril.Chin@Sun.COMprintf "%T: # Waiting to obtain a shared lock...\n"
239*8462SApril.Chin@Sun.COMfs.lock_shared
240*8462SApril.Chin@Sun.COMprintf "%T: # Obtained shared lock\n"
241*8462SApril.Chin@Sun.COM
242*8462SApril.Chin@Sun.COMprintf "fs.locked_exclusive=%s, fs.locked_shared=%s\n" "${fs.locked_exclusive}" "${fs.locked_shared}"
243*8462SApril.Chin@Sun.COM
244*8462SApril.Chin@Sun.COMls -lad /tmp/filemutex*/*
245*8462SApril.Chin@Sun.COM
246*8462SApril.Chin@Sun.COMprintf "%T: # Executing child which runs printf '|%%s|\\\n' 'hello' 'world' inside a synchronized section\n"
247*8462SApril.Chin@Sun.COM(
248*8462SApril.Chin@Sun.COM	filemutex_t child_fs
249*8462SApril.Chin@Sun.COM
250*8462SApril.Chin@Sun.COM	child_fs.create_child "${mymutexname}"
251*8462SApril.Chin@Sun.COM
252*8462SApril.Chin@Sun.COM	child_fs.synchronized printf '|%s|\n' 'hello' 'world'
253*8462SApril.Chin@Sun.COM) &
254*8462SApril.Chin@Sun.COM
255*8462SApril.Chin@Sun.COMprintf "%T: # Sleeping 5 secs while holding the shared lock...\n"
256*8462SApril.Chin@Sun.COMsleep 5.
257*8462SApril.Chin@Sun.COM
258*8462SApril.Chin@Sun.COMprintf "%T: # Releasing shared lock...\n"
259*8462SApril.Chin@Sun.COMfs.unlock
260*8462SApril.Chin@Sun.COM
261*8462SApril.Chin@Sun.COMsleep 5.
262*8462SApril.Chin@Sun.COMprint "# Destroying lock..."
263*8462SApril.Chin@Sun.COMfs.destroy
264*8462SApril.Chin@Sun.COM
265*8462SApril.Chin@Sun.COMprint "## Done."
266*8462SApril.Chin@Sun.COM
267*8462SApril.Chin@Sun.COMexit 0
268