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