Subversion Repositories HelenOS

Rev

Rev 2430 | 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
2456 hudecek 45
#define RCU_MAX_I 5000
2430 hudecek 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;
2456 hudecek 72
    while (true) {
2430 hudecek 73
        //entering read critical section
2400 hudecek 74
        rcu_read_lock();
2430 hudecek 75
        //proper dereferencing
76
        i = rcu_dereference_pointer(first).number;
77
        for (cur = rcu_dereference_pointer(first).next; cur != NULL; cur = cur->next) {
78
            i += cur->number;
2400 hudecek 79
        }
80
        rcu_read_unlock();
2456 hudecek 81
        if (i>RCU_MAX_I*RCU_MAX_I || cfinished>0) {
82
            if (!gquiet)
83
                printf("@");
2400 hudecek 84
            break;
85
        }
2430 hudecek 86
        thread_usleep(THREADS_SLEEP_LENGTH);
2400 hudecek 87
    }
2456 hudecek 88
    //we must achieve some kind of synchronization gcc wont emit inc [cfinished]
89
    spinlock_lock(&write_lock);
2430 hudecek 90
    cfinished++;
2456 hudecek 91
    spinlock_unlock(&write_lock);
2309 hudecek 92
}
2400 hudecek 93
 
94
static void writer(void* a)
95
{
96
    a = (a);
97
    data_t* cur;
2430 hudecek 98
    data_t* newdata;
99
    data_t* oldata;
2400 hudecek 100
    rcu_callback_list_t* rcudata;
101
    int i = 0;
2456 hudecek 102
    while (true) {
2430 hudecek 103
        //we must allocate the rcu structure each time, because it gets freed after the callback
104
        //we allocate it outside any critical section, as it could block
105
        rcudata = malloc(sizeof(rcu_callback_list_t),0);
106
        rcu_read_lock();
2400 hudecek 107
        i = rcu_dereference_pointer(first).number;
2430 hudecek 108
        for (cur = rcu_dereference_pointer(first).next; cur != NULL; cur = cur->next) {
109
            i += cur->number;
2400 hudecek 110
        }
111
        rcu_read_unlock();
2456 hudecek 112
        if (!gquiet && false)
2400 hudecek 113
            printf("i%d ",i);
2456 hudecek 114
        if (i>RCU_MAX_I*RCU_MAX_I || cfinished>0) {
115
            if (!gquiet)
116
                printf("!");
2400 hudecek 117
            break;
118
        }
119
 
2430 hudecek 120
        //insert a new member 
2400 hudecek 121
        newdata = malloc(sizeof(data_t),0);
2430 hudecek 122
        newdata->number = (i/(RCU_MAX_I/2))+1;
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);
2456 hudecek 128
        if (!gquiet && false)
2430 hudecek 129
            printf("prepending:%x,n:%d ", newdata, newdata->number);
2400 hudecek 130
        spinlock_unlock(&write_lock);
131
 
2430 hudecek 132
 
133
        //replace a random member
134
        //we have to lock the spinlock now, because we'll use the cur pointer later 
135
        //RCU doesn't provide guarantee that cur->next will point to a member of the list
136
        //note that read critical section DOES guarantee that the *cur will be allocated space
137
        //with current or past version of some member of the list
138
        //However, that is not sufficient as we would be changing old version, which would be freed later on
139
        spinlock_lock(&write_lock);
140
        for (cur = first; cur->next != NULL && cur->next->number >= (i/(RCU_MAX_I/2)); cur = rcu_dereference_pointer(cur).next) {
2400 hudecek 141
        }
2430 hudecek 142
        if (cur->next != NULL) {
143
            newdata = malloc(sizeof(data_t),0);
144
            //the change of number member could be done atomically, its here just to simulate some real work
2456 hudecek 145
            newdata->number = (i/(RCU_MAX_I/2))+10;
2430 hudecek 146
            newdata->next = cur->next->next;
147
            oldata = cur->next;
2456 hudecek 148
            if (!gquiet && false)
2430 hudecek 149
                printf("free:%x,n:%d ", cur->next, cur->next->number);
150
            rcu_assign_pointer(cur->next, newdata);
151
            //free the old member when it is safe (i.e. no references are held)
2400 hudecek 152
            rcu_sync_callback(&rcu_callback_free, oldata, rcudata);
153
            spinlock_unlock(&write_lock);
154
        }
155
    }
2456 hudecek 156
    //we must achieve some kind of synchronization gcc wont emit inc [cfinished]
157
    spinlock_lock(&write_lock);
2430 hudecek 158
    cfinished++;
2456 hudecek 159
    spinlock_unlock(&write_lock);
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);
2456 hudecek 198
    if (!gquiet)
199
        printf("\nfinished all threads!\n");
2430 hudecek 200
    //free the list
201
    for(cur=first->next;cur!=NULL;) {
2400 hudecek 202
        oldata = cur->next;
203
        free(cur);
204
        cur = oldata;
205
    }
206
    free(first);
2309 hudecek 207
    return NULL;
208
 
209
}
210
 
211