1707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner/*
2707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * QEMU System Emulator
3707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner *
4707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * Copyright (c) 2003-2008 Fabrice Bellard
5707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner *
6707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * Permission is hereby granted, free of charge, to any person obtaining a copy
7707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * of this software and associated documentation files (the "Software"), to deal
8707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * in the Software without restriction, including without limitation the rights
9707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * copies of the Software, and to permit persons to whom the Software is
11707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * furnished to do so, subject to the following conditions:
12707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner *
13707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * The above copyright notice and this permission notice shall be included in
14707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * all copies or substantial portions of the Software.
15707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner *
16707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * THE SOFTWARE.
23707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner */
24707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner
25707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner#include "qemu-common.h"
26707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner#include "qemu-aio.h"
27707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner
28707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner/*
29707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * An AsyncContext protects the callbacks of AIO requests and Bottom Halves
30707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * against interfering with each other. A typical example is qcow2 that accepts
31707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * asynchronous requests, but relies for manipulation of its metadata on
32707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * synchronous bdrv_read/write that doesn't trigger any callbacks.
33707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner *
34707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * However, these functions are often emulated using AIO which means that AIO
35707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * callbacks must be run - but at the same time we must not run callbacks of
36707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * other requests as they might start to modify metadata and corrupt the
37707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * internal state of the caller of bdrv_read/write.
38707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner *
39707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * To achieve the desired semantics we switch into a new AsyncContext.
40707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * Callbacks must only be run if they belong to the current AsyncContext.
41707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * Otherwise they need to be queued until their own context is active again.
42707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * This is how you can make qemu_aio_wait() wait only for your own callbacks.
43707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner *
44707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * The AsyncContexts form a stack. When you leave a AsyncContexts, you always
45707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * return to the old ("parent") context.
46707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner */
47707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turnerstruct AsyncContext {
48707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    /* Consecutive number of the AsyncContext (position in the stack) */
49707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    int id;
50707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner
51707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    /* Anchor of the list of Bottom Halves belonging to the context */
52707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    struct QEMUBH *first_bh;
53707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner
54707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    /* Link to parent context */
55707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    struct AsyncContext *parent;
56707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner};
57707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner
58707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner/* The currently active AsyncContext */
59707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turnerstatic struct AsyncContext *async_context = &(struct AsyncContext) { 0 };
60707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner
61707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner/*
62707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * Enter a new AsyncContext. Already scheduled Bottom Halves and AIO callbacks
63707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * won't be called until this context is left again.
64707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner */
65707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turnervoid async_context_push(void)
66707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner{
67707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    struct AsyncContext *new = qemu_mallocz(sizeof(*new));
68707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    new->parent = async_context;
69707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    new->id = async_context->id + 1;
70707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    async_context = new;
71707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner}
72707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner
73707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner/* Run queued AIO completions and destroy Bottom Half */
74707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turnerstatic void bh_run_aio_completions(void *opaque)
75707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner{
76707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    QEMUBH **bh = opaque;
77707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    qemu_bh_delete(*bh);
78707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    qemu_free(bh);
79707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    qemu_aio_process_queue();
80707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner}
81707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner/*
82707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * Leave the currently active AsyncContext. All Bottom Halves belonging to the
83707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * old context are executed before changing the context.
84707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner */
85707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turnervoid async_context_pop(void)
86707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner{
87707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    struct AsyncContext *old = async_context;
88707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    QEMUBH **bh;
89707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner
90707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    /* Flush the bottom halves, we don't want to lose them */
91707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    while (qemu_bh_poll());
92707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner
93707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    /* Switch back to the parent context */
94707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    async_context = async_context->parent;
95707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    qemu_free(old);
96707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner
97707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    if (async_context == NULL) {
98707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner        abort();
99707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    }
100707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner
101707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    /* Schedule BH to run any queued AIO completions as soon as possible */
102707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    bh = qemu_malloc(sizeof(*bh));
103707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    *bh = qemu_bh_new(bh_run_aio_completions, bh);
104707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    qemu_bh_schedule(*bh);
105707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner}
106707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner
107707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner/*
108707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner * Returns the ID of the currently active AsyncContext
109707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner */
110707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turnerint get_async_context_id(void)
111707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner{
112707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    return async_context->id;
113707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner}
114707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner
115707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner/***********************************************************/
116707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner/* bottom halves (can be seen as timers which expire ASAP) */
117707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner
118707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turnerstruct QEMUBH {
119707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    QEMUBHFunc *cb;
120707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    void *opaque;
121707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    int scheduled;
122707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    int idle;
123707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    int deleted;
124707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    QEMUBH *next;
125707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner};
126707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner
127707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' TurnerQEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque)
128707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner{
129707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    QEMUBH *bh;
130707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    bh = qemu_mallocz(sizeof(QEMUBH));
131707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    bh->cb = cb;
132707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    bh->opaque = opaque;
133707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    bh->next = async_context->first_bh;
134707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    async_context->first_bh = bh;
135707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    return bh;
136707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner}
137707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner
138707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turnerint qemu_bh_poll(void)
139707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner{
140707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    QEMUBH *bh, **bhp;
141707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    int ret;
142707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner
143707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    ret = 0;
144707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    for (bh = async_context->first_bh; bh; bh = bh->next) {
145707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner        if (!bh->deleted && bh->scheduled) {
146707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner            bh->scheduled = 0;
147707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner            if (!bh->idle)
148707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner                ret = 1;
149707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner            bh->idle = 0;
150707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner            bh->cb(bh->opaque);
151707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner        }
152707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    }
153707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner
154707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    /* remove deleted bhs */
155707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    bhp = &async_context->first_bh;
156707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    while (*bhp) {
157707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner        bh = *bhp;
158707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner        if (bh->deleted) {
159707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner            *bhp = bh->next;
160707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner            qemu_free(bh);
161707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner        } else
162707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner            bhp = &bh->next;
163707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    }
164707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner
165707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    return ret;
166707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner}
167707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner
168707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turnervoid qemu_bh_schedule_idle(QEMUBH *bh)
169707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner{
170707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    if (bh->scheduled)
171707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner        return;
172707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    bh->scheduled = 1;
173707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    bh->idle = 1;
174707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner}
175707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner
176707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turnervoid qemu_bh_schedule(QEMUBH *bh)
177707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner{
178707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    if (bh->scheduled)
179707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner        return;
180707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    bh->scheduled = 1;
181707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    bh->idle = 0;
182707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    /* stop the currently executing CPU to execute the BH ASAP */
183707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    qemu_notify_event();
184707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner}
185707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner
186707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turnervoid qemu_bh_cancel(QEMUBH *bh)
187707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner{
188707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    bh->scheduled = 0;
189707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner}
190707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner
191707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turnervoid qemu_bh_delete(QEMUBH *bh)
192707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner{
193707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    bh->scheduled = 0;
194707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    bh->deleted = 1;
195707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner}
196707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner
197707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turnervoid qemu_bh_update_timeout(int *timeout)
198707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner{
199707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    QEMUBH *bh;
200707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner
201707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    for (bh = async_context->first_bh; bh; bh = bh->next) {
202707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner        if (!bh->deleted && bh->scheduled) {
203707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner            if (bh->idle) {
204707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner                /* idle bottom halves will be polled at least
205707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner                 * every 10ms */
206707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner                *timeout = MIN(10, *timeout);
207707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner            } else {
208707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner                /* non-idle bottom halves will be executed
209707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner                 * immediately */
210707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner                *timeout = 0;
211707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner                break;
212707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner            }
213707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner        }
214707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner    }
215707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner}
216707c8a8975842105dd04d61a416ee175d033b94dDavid 'Digit' Turner
217