From 69bf1bfc21517fd000202522d076ba6c1c90a579 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Boisnard?= Date: Fri, 10 Jan 2014 19:47:08 +0100 Subject: Fix hostDomainGenerator.sh concurrency issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BZ: 151780 When multiple process run hostDomainGenerator.sh, some race conditions occur, despite current locking mechanism. In consequence, the script has been modified in order to make sure that available socket ports can be chosen for test-platform and PFW processes: - portAllocator.py: python script which creates a new socket using a random available port, and displays the port chosen. This port number is then pushed to the end of the available ports list by the system. - hostDomainGenerator.sh: uses portAllocator.py to get free ports for test-platform and the PFW. Fixed multiple issues as well: - When an error occur, a cleanup function was called and sometimes would kill concurrent processes. Now, only the test-platform and PFW instances started by the current hostDomainGenerator.sh script will be stopped, - Test-platform startup was not checked correctly, - The creation of the symlink to libremote-processor.so sometimes failed, leading to an early cleanup. Change-Id: I33b4b2ad5a6c2bbc6f8283222eb2f16f05286734 Signed-off-by: Frédéric Boisnard --- tools/xmlGenerator/Android.mk | 8 ++ tools/xmlGenerator/hostDomainGenerator.sh | 125 ++++++++++++++++-------------- tools/xmlGenerator/portAllocator.py | 41 ++++++++++ 3 files changed, 114 insertions(+), 60 deletions(-) create mode 100755 tools/xmlGenerator/portAllocator.py (limited to 'tools') diff --git a/tools/xmlGenerator/Android.mk b/tools/xmlGenerator/Android.mk index 87144a3..328f70f 100644 --- a/tools/xmlGenerator/Android.mk +++ b/tools/xmlGenerator/Android.mk @@ -38,11 +38,19 @@ LOCAL_IS_HOST_MODULE := true include $(BUILD_PREBUILT) include $(CLEAR_VARS) +LOCAL_MODULE := portAllocator.py +LOCAL_SRC_FILES := $(LOCAL_MODULE) +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_IS_HOST_MODULE := true +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) LOCAL_MODULE := hostDomainGenerator.sh LOCAL_SRC_FILES := $(LOCAL_MODULE) LOCAL_REQUIRED_MODULES := \ PFWScriptGenerator.py \ hostConfig.py \ + portAllocator.py \ test-platform_host \ remote-process_host LOCAL_MODULE_CLASS := EXECUTABLES diff --git a/tools/xmlGenerator/hostDomainGenerator.sh b/tools/xmlGenerator/hostDomainGenerator.sh index b67261b..9bf8606 100755 --- a/tools/xmlGenerator/hostDomainGenerator.sh +++ b/tools/xmlGenerator/hostDomainGenerator.sh @@ -55,9 +55,11 @@ remoteProcess="remote-process_host" hostConfig="hostConfig.py" PFWScriptGenerator="PFWScriptGenerator.py" +portAllocator="portAllocator.py" TPHost=localhost PFWHost=localhost +TPCreated=false HostRoot="$ANDROID_HOST_OUT" TargetRoot="$ANDROID_PRODUCT_OUT/system" @@ -68,12 +70,10 @@ PFWSocket=5000 PFWStartTimeout=60 tmpFile=$(mktemp) -testPlatformPID=0 -lockFile="/var/lock/.hostDomainGenerator.lockfile" # [Workaround] # The build system does not preserve execution right in external prebuild -for file in "$testPlatform" "$remoteProcess" "$hostConfig" "$PFWScriptGenerator" +for file in "$testPlatform" "$remoteProcess" "$hostConfig" "$PFWScriptGenerator" "$portAllocator" do chmod +x "${HostRoot}/bin/${file}" done @@ -85,16 +85,17 @@ export LD_LIBRARY_PATH="$HostRoot/lib:${LD_LIBRARY_PATH:-}" clean_up () { status=$? - if test $testPlatformPID != 0 + # Exit the test-platform only if it was created by this process + if $TPCreated then - echo "Clean sub process: $testPlatformPID" - kill $testPlatformPID 2>&1 + echo "Exiting test-platform listening on port $TPSocket" + $remoteProcess $TPHost $TPSocket exit fi - rm "$tmpFile" || true - # Delete the lockfile - rm -f $lockFile || true + echo "Cleaning $tmpFile ..." + rm "$tmpFile" || true + echo "Cleaning status: $status ..." return $status } @@ -108,12 +109,22 @@ linkLibrary () { local src="$1" local dest="$2" local path=$(find $HostRoot/lib -name "$src") - if test "$(basename "$path")" != "$dest" + + # Check that both names are different, otherwise there is an error + if ! test "$(basename "$path")" != "$dest" then - ln -fs "$src" "$(dirname "$path")/$dest" - else + echo "Cannot link $dest to $src !" return 1 fi + + # Check that destination file does not already exist + if ! test -f "$(dirname "$path")/$dest" + then + # Create the symlink. Do not force if it has been created after the previous + # test, in this case simply ignore the error + ln -s "$src" "$(dirname "$path")/$dest" || true + fi + return 0 } # The retry function will execute the provided command nbRety times util success. @@ -137,34 +148,25 @@ formatConfigFile () { "$hostConfig" $PFWSocket "$(readlink -f "$(dirname "$1")")" <"$1" } -# Test if socket is currently used -portIsInUse () { - port=$1 - test $(ss -an | grep ":${port}" | wc --lines) -gt 0 -} - # The initTestPlatform starts a testPlatform instance with the config file given in argument. # It will also set the PFWSocket global variable to the PFW remote processor listening socket. initTestPlatform () { # Format the PFW config file formatConfigFile "$1" >"$tmpFile" - # Check port is free - echo "Checking port $TPSocket for TestPlatform" - ! portIsInUse $TPSocket || return 4 - echo "Port $TPSocket is available for TestPlatform" - # Start test platform - $testPlatform "$tmpFile" $TPSocket 2>&5 & - testPlatformPID=$! + echo "Starting test-platform on port $TPSocket ..." + $testPlatform -d "$tmpFile" $TPSocket 2>&5 - if ! retry "$remoteProcess $TPHost $TPSocket help" 2 0.1 + res=$? + if test $res -ne 0 then echo "Unable to launch the simulation platform (using socket $TPSocket)" >&5 return 4 - else - echo "Test platform successfuly loaded!" fi + + echo "Test platform successfuly loaded!" + return 0 } # Execute a command for each input line, stopping in case of error @@ -183,25 +185,40 @@ launchTestPlatform () { $TPSendCommand setFailureOnMissingSubsystem false $TPSendCommand setFailureOnFailedSettingsLoad false - # Check port is free - echo "Checking port $PFWSocket for PFW" - ! portIsInUse $PFWSocket || return 5 - echo "Port $PFWSocket is available for PFW" - + echo "Asking test-platform (port $TPSocket) to start a new PFW instance (listening on port $PFWSocket) ..." $TPSendCommand start - if ! retry "$remoteProcess $PFWHost $PFWSocket help" 2 0.1 + res=$? + if test $res -ne 0 then - echo "Unable to launch the parameter framework (using socket $PFWSocket)" >&5 + echo "Unable to launch the parameter framework (using port $PFWSocket)" >&5 return 5 - else - echo "Parameter framework successfully started!" fi -} + echo "Parameter framework successfully started!" + return 0 +} startPFW () { - initTestPlatform "$PFWconfigurationFilePath" && - launchTestPlatform "$CriterionFilePath" + # Init the test-platform + initTestPlatform "$PFWconfigurationFilePath" || return 1 + TPCreated=true + + # Ask the test-platform to start the PFW + if ! launchTestPlatform "$CriterionFilePath" + then + # If PFW didn't start, exit the current test-platform, and return failure in + # order to choose new socket ports + echo "Exiting test-platform listening on port $TPSocket" + $remoteProcess $TPHost $TPSocket exit + TPCreated=false + return 1 + fi +} + +# Get a new pair of available ports for TP and PFW sockets +changeSocketsPorts() { + TPSocket=$($portAllocator) || return 1 + PFWSocket=$($portAllocator) || return 1 } # Start the pfw using different socket if it fails @@ -209,15 +226,19 @@ safeStartPFW () { local retry=0 local nbRetry=10 + # Choose a new pair of socket ports + changeSocketsPorts + echo "Trying to start test-platform and PFW, with socket $TPSocket and $PFWSocket" + while ! startPFW do (($retry < $nbRetry)) || return 1 retry=$(($retry + 1)) - clean_up || true - TPSocket=$(($TPSocket + 10)) - PFWSocket=$(($PFWSocket + 10)) - echo "unable to start PFW, try again with socket $TPSocket and $PFWSocket" + # Choose a new pair of socket ports + changeSocketsPorts || continue + + echo "Unable to start PFW, try again with socket $TPSocket and $PFWSocket" done } @@ -229,25 +250,9 @@ deleteEscapedNewLines () { linkLibrary libremote-processor_host.so libremote-processor.so # Start test platform and the PFW -# -# Creation of a critical section to ensure that the startup of the PFW (involving -# sockets creation to communicate with the test platform and the PFW) is serialized -# between potential concurrent execution of this script -# -# Acquire an exclusive lock on the file descriptor 200 -# The file used for locking must be created with non restrictive permissions, so -# that the script can be used by multiple users. -exec 200>$lockFile -chmod -f 777 $lockFile || true - -flock --timeout $PFWStartTimeout 200 - # Start the pfw using different socket if it fails safeStartPFW -# Release the lock -flock --unlock 200 - PFWSendCommand="$remoteProcess $PFWHost $PFWSocket" $PFWSendCommand setTuningMode on diff --git a/tools/xmlGenerator/portAllocator.py b/tools/xmlGenerator/portAllocator.py new file mode 100755 index 0000000..45c527d --- /dev/null +++ b/tools/xmlGenerator/portAllocator.py @@ -0,0 +1,41 @@ +#!/usr/bin/python2 +# +# INTEL CONFIDENTIAL +# Copyright (c) 2013 Intel +# Corporation All Rights Reserved. +# +# The source code contained or described herein and all documents related to +# the source code ("Material") are owned by Intel Corporation or its suppliers +# or licensors. Title to the Material remains with Intel Corporation or its +# suppliers and licensors. The Material contains trade secrets and proprietary +# and confidential information of Intel or its suppliers and licensors. The +# Material is protected by worldwide copyright and trade secret laws and +# treaty provisions. No part of the Material may be used, copied, reproduced, +# modified, published, uploaded, posted, transmitted, distributed, or +# disclosed in any way without Intel's prior express written permission. +# +# No license under any patent, copyright, trade secret or other intellectual +# property right is granted to or conferred upon you by disclosure or delivery +# of the Materials, either expressly, by implication, inducement, estoppel or +# otherwise. Any license under such intellectual property rights must be +# express and approved by Intel in writing. +# + +import sys, socket + +serversock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +try: + # Create a listening socket on a random available port + serversock.bind(('localhost',0)) + serversock.listen(0) + + # Print the chosen port + print(serversock.getsockname()[1]) + serversock.close() + +except socket.error, (errno,message): + print("Socket creation error " + str(errno) + ": " + message) + if serversock: + serversock.close() + sys.exit(1) + -- cgit v1.1