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