summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/net/lib/sh/defer.sh
blob: 47ab78c4d4656eb63f1940cafbbda60269dd645e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0

# Whether to pause and allow debugging when an executed deferred command has a
# non-zero exit code.
: "${DEFER_PAUSE_ON_FAIL:=no}"

# map[(scope_id,track,cleanup_id) -> cleanup_command]
# track={d=default | p=priority}
declare -A __DEFER__JOBS

# map[(scope_id,track) -> # cleanup_commands]
declare -A __DEFER__NJOBS

# scope_id of the topmost scope.
__DEFER__SCOPE_ID=0

__defer__ndefer_key()
{
	local track=$1; shift

	echo $__DEFER__SCOPE_ID,$track
}

__defer__defer_key()
{
	local track=$1; shift
	local defer_ix=$1; shift

	echo $__DEFER__SCOPE_ID,$track,$defer_ix
}

__defer__ndefers()
{
	local track=$1; shift

	echo ${__DEFER__NJOBS[$(__defer__ndefer_key $track)]}
}

__defer__run()
{
	local track=$1; shift
	local defer_ix=$1; shift
	local defer_key=$(__defer__defer_key $track $defer_ix)
	local ret

	eval ${__DEFER__JOBS[$defer_key]}
	ret=$?

	if [[ "$DEFER_PAUSE_ON_FAIL" == yes && "$ret" -ne 0 ]]; then
		echo "Deferred command (track $track index $defer_ix):"
		echo "	${__DEFER__JOBS[$defer_key]}"
		echo "... ended with an exit status of $ret"
		echo "Hit enter to continue, 'q' to quit"
		read a
		[[ "$a" == q ]] && exit 1
	fi

	unset __DEFER__JOBS[$defer_key]
}

__defer__schedule()
{
	local track=$1; shift
	local ndefers=$(__defer__ndefers $track)
	local ndefers_key=$(__defer__ndefer_key $track)
	local defer_key=$(__defer__defer_key $track $ndefers)
	local defer="${@@Q}"

	__DEFER__JOBS[$defer_key]="$defer"
	__DEFER__NJOBS[$ndefers_key]=$((ndefers + 1))
}

__defer__scope_wipe()
{
	__DEFER__NJOBS[$(__defer__ndefer_key d)]=0
	__DEFER__NJOBS[$(__defer__ndefer_key p)]=0
}

defer_scope_push()
{
	((__DEFER__SCOPE_ID++))
	__defer__scope_wipe
}

defer_scope_pop()
{
	local defer_ix

	for ((defer_ix=$(__defer__ndefers p); defer_ix-->0; )); do
		__defer__run p $defer_ix
	done

	for ((defer_ix=$(__defer__ndefers d); defer_ix-->0; )); do
		__defer__run d $defer_ix
	done

	__defer__scope_wipe
	((__DEFER__SCOPE_ID--))
}

defer()
{
	__defer__schedule d "$@"
}

defer_prio()
{
	__defer__schedule p "$@"
}

defer_scopes_cleanup()
{
	while ((__DEFER__SCOPE_ID >= 0)); do
		defer_scope_pop
	done
}

in_defer_scope()
{
	local ret

	defer_scope_push
	"$@"
	ret=$?
	defer_scope_pop

	return $ret
}

__defer__scope_wipe