Subversion Repositories HelenOS

Rev

Rev 2400 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2309 hudecek 1
/*
2
 * Copyright (c) 2007 Jan Hudecek
3
 * Copyright (c) 2005 Jakub Jermar
4
 * All rights reserved.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions
8
 * are met:
9
 *
10
 * - Redistributions of source code must retain the above copyright
11
 *   notice, this list of conditions and the following disclaimer.
12
 * - Redistributions in binary form must reproduce the above copyright
13
 *   notice, this list of conditions and the following disclaimer in the
14
 *   documentation and/or other materials provided with the distribution.
15
 * - The name of the author may not be used to endorse or promote products
16
 *   derived from this software without specific prior written permission.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 */
29
 
2430 hudecek 30
 
31
/* This is RCU thorough test. It should provide basic guidelines on how to use
32
this implementation of RCU */
33
 
2309 hudecek 34
#include <synch/rcu.h>
35
#include <print.h>
36
#include <test.h>
37
#include <arch/types.h>
38
#include <proc/tasklet.h>
39
#include <arch/barrier.h>
40
#include <arch.h>
41
#include <preemption.h>
2400 hudecek 42
#include <proc/thread.h>
43
 
2430 hudecek 44
//number of nodes in the list. The sum of the list will grow up to RCU_MAX_I^2
45
#define RCU_MAX_I 500
46
//number of reader and writer threads in the test
2400 hudecek 47
#define READER_THREADS 10
48
#define WRITER_THREADS 10
2430 hudecek 49
//a sleep separates iterations of reading 
50
#define THREADS_SLEEP_LENGTH 50
2400 hudecek 51
 
2430 hudecek 52
//shared flag specifying whether we should be quiet
2309 hudecek 53
bool gquiet;
2430 hudecek 54
//count of finished threads
55
volatile int cfinished;
56
//the list we will manage with RCU
2400 hudecek 57
typedef struct data_struc {
58
    int number;
59
    struct data_struc* next;
60
} data_t;
2309 hudecek 61
 
2400 hudecek 62
data_t* first;
63
SPINLOCK_INITIALIZE(write_lock);
64
 
65
 
2430 hudecek 66
/** this thread will try to read from the list */
2400 hudecek 67
static void reader(void* a)
2309 hudecek 68
{
2400 hudecek 69
    a = (a);
70
    data_t* cur;
71
    int i = 0;
72
    while (true)
73
    {
2430 hudecek 74
        //entering read critical section
2400 hudecek 75
        rcu_read_lock();
2430 hudecek 76
        //proper dereferencing
77
        i = rcu_dereference_pointer(first).number;
78
        for (cur = rcu_dereference_pointer(first).next; cur != NULL; cur = cur->next) {
79
            i += cur->number;
2400 hudecek 80
        }
81
        rcu_read_unlock();
2430 hudecek 82
        if (i>RCU_MAX_I*RCU_MAX_I || cfinished>0)
2400 hudecek 83
        {
84
            printf("@");
85
            break;
86
        }
2430 hudecek 87
        thread_usleep(THREADS_SLEEP_LENGTH);
2400 hudecek 88
    }
2430 hudecek 89
    cfinished++;
2309 hudecek 90
}
2400 hudecek 91
 
92
static void writer(void* a)
93
{
94
    a = (a);
95
    data_t* cur;
2430 hudecek 96
    data_t* newdata;
97
    data_t* oldata;
2400 hudecek 98
    rcu_callback_list_t* rcudata;
99
    int i = 0;
100
    while (true)
101
    {
2430 hudecek 102
        //we must allocate the rcu structure each time, because it gets freed after the callback
103
        //we allocate it outside any critical section, as it could block
104
        rcudata = malloc(sizeof(rcu_callback_list_t),0);
105
        rcu_read_lock();
2400 hudecek 106
        i = rcu_dereference_pointer(first).number;
2430 hudecek 107
        for (cur = rcu_dereference_pointer(first).next; cur != NULL; cur = cur->next) {
108
            i += cur->number;
2400 hudecek 109
        }
110
        rcu_read_unlock();
111
        if (!gquiet)
112
            printf("i%d ",i);
2430 hudecek 113
        if (i>RCU_MAX_I*RCU_MAX_I || cfinished>0)
2400 hudecek 114
        {
115
            printf("!");
116
            break;
117
        }
118
 
2430 hudecek 119
        //insert a new member 
2400 hudecek 120
        newdata = malloc(sizeof(data_t),0);
2430 hudecek 121
        newdata->number = (i/(RCU_MAX_I/2))+1;
2400 hudecek 122
        rcu_read_lock();
2430 hudecek 123
        //we have to acquire the lock for writing to the structure
2400 hudecek 124
        spinlock_lock(&write_lock);
125
        newdata->next = first;
2430 hudecek 126
        //rcu_assign_pointer takes care of the necessary write barriers
2400 hudecek 127
        rcu_assign_pointer(first, newdata);
2430 hudecek 128
        if (!gquiet)
129
            printf("prepending:%x,n:%d ", newdata, newdata->number);
2400 hudecek 130
        spinlock_unlock(&write_lock);
131
        rcu_read_unlock();
132
 
2430 hudecek 133
 
134
        //replace a random member
2400 hudecek 135
        rcu_read_lock();
2430 hudecek 136
        //we have to lock the spinlock now, because we'll use the cur pointer later 
137
        //RCU doesn't provide guarantee that cur->next will point to a member of the list
138
        //note that read critical section DOES guarantee that the *cur will be allocated space
139
        //with current or past version of some member of the list
140
        //However, that is not sufficient as we would be changing old version, which would be freed later on
141
        spinlock_lock(&write_lock);
142
        for (cur = first; cur->next != NULL && cur->next->number >= (i/(RCU_MAX_I/2)); cur = rcu_dereference_pointer(cur).next) {
2400 hudecek 143
        }
2430 hudecek 144
        if (cur->next != NULL) {
145
            newdata = malloc(sizeof(data_t),0);
146
            //the change of number member could be done atomically, its here just to simulate some real work
147
            newdata->number = (i/(RCU_MAX_I/2))+5;
148
            newdata->next = cur->next->next;
149
            oldata = cur->next;
150
            if (!gquiet)
151
                printf("free:%x,n:%d ", cur->next, cur->next->number);
152
            rcu_assign_pointer(cur->next, newdata);
153
            //free the old member when it is safe (i.e. no references are held)
2400 hudecek 154
            rcu_sync_callback(&rcu_callback_free, oldata, rcudata);
155
            spinlock_unlock(&write_lock);
156
        }
2430 hudecek 157
        rcu_read_unlock(); 
2400 hudecek 158
    }
2430 hudecek 159
    cfinished++;
2400 hudecek 160
}
161
 
2309 hudecek 162
char * test_rcu1(bool quiet)
163
{
164
    gquiet = quiet;
2400 hudecek 165
    data_t* cur, *oldata;
166
    int i;
167
    thread_t* t;
2430 hudecek 168
 
169
    //allocate and initialize the list
2400 hudecek 170
    first = malloc(sizeof(data_t),0);
2430 hudecek 171
    first->number = 0;
172
    cur = first;
173
    for (i=1;i<RCU_MAX_I;i++) {
174
        cur->next = malloc(sizeof(data_t),0);
175
        cur = cur->next;
176
        cur->number = i;
177
    }
178
    cur->next = NULL;
2400 hudecek 179
 
2430 hudecek 180
    //initialize the counter of finished threads
181
    cfinished=0;
182
 
183
    //start the writers
2400 hudecek 184
    for(i = 0; i< WRITER_THREADS; i++) {
2430 hudecek 185
        t=thread_create(writer,NULL, TASK, 0, "writerthread", false );
2400 hudecek 186
        if (t != NULL)
187
            thread_ready(t);
188
    }
189
 
2430 hudecek 190
    //start the readers
2400 hudecek 191
    for(i = 0; i< READER_THREADS; i++) {
2430 hudecek 192
        t=thread_create(reader,NULL, TASK, 0, "readerthread", false );
2400 hudecek 193
        if (t != NULL)
194
            thread_ready(t);
195
    }
2430 hudecek 196
    //wait for completion
197
    while (cfinished<WRITER_THREADS+READER_THREADS);
198
    printf("\nfinished all threads!\n");
199
    //free the list
200
    for(cur=first->next;cur!=NULL;) {
2400 hudecek 201
        oldata = cur->next;
202
        free(cur);
203
        cur = oldata;
204
    }
205
    free(first);
2309 hudecek 206
    return NULL;
207
 
208
}
209
 
210