Subversion Repositories HelenOS

Rev

Rev 2400 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 2400 Rev 2430
Line 25... Line 25...
25
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
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
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.
27
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 */
28
 */
29
 
29
 
-
 
30
 
-
 
31
/* This is RCU thorough test. It should provide basic guidelines on how to use
-
 
32
this implementation of RCU */
-
 
33
 
30
#include <synch/rcu.h>
34
#include <synch/rcu.h>
31
#include <print.h>
35
#include <print.h>
32
#include <test.h>
36
#include <test.h>
33
#include <arch/types.h>
37
#include <arch/types.h>
34
#include <proc/tasklet.h>
38
#include <proc/tasklet.h>
35
#include <arch/barrier.h>
39
#include <arch/barrier.h>
36
#include <arch.h>
40
#include <arch.h>
37
#include <preemption.h>
41
#include <preemption.h>
38
#include <proc/thread.h>
42
#include <proc/thread.h>
39
 
43
 
40
 
-
 
-
 
44
//number of nodes in the list. The sum of the list will grow up to RCU_MAX_I^2
41
#define RCU_MAX_I 1000
45
#define RCU_MAX_I 500
-
 
46
//number of reader and writer threads in the test
42
#define READER_THREADS 10
47
#define READER_THREADS 10
43
#define WRITER_THREADS 10
48
#define WRITER_THREADS 10
-
 
49
//a sleep separates iterations of reading 
44
#define THREADS_SLEEP_LENGTH (uint32_t)50
50
#define THREADS_SLEEP_LENGTH 50
45
 
51
 
-
 
52
//shared flag specifying whether we should be quiet
46
bool gquiet;
53
bool gquiet;
-
 
54
//count of finished threads
47
volatile bool finished;
55
volatile int cfinished;
-
 
56
//the list we will manage with RCU
48
typedef struct data_struc {
57
typedef struct data_struc {
49
    int number;
58
    int number;
50
    struct data_struc* next;
59
    struct data_struc* next;
51
} data_t;
60
} data_t;
52
 
61
 
53
data_t* first;
62
data_t* first;
54
SPINLOCK_INITIALIZE(write_lock);
63
SPINLOCK_INITIALIZE(write_lock);
55
 
64
 
56
 
65
 
57
 
-
 
-
 
66
/** this thread will try to read from the list */
58
static void reader(void* a)
67
static void reader(void* a)
59
{
68
{
60
    a = (a);
69
    a = (a);
61
    data_t* cur;
70
    data_t* cur;
62
    int i = 0;
71
    int i = 0;
63
    while (true)
72
    while (true)
64
    {
73
    {
65
        i = 0;
74
        //entering read critical section
66
        rcu_read_lock();
75
        rcu_read_lock();
-
 
76
        //proper dereferencing
-
 
77
        i = rcu_dereference_pointer(first).number;
67
        for (cur = rcu_dereference_pointer(first).next; cur != first; cur = cur->next) {
78
        for (cur = rcu_dereference_pointer(first).next; cur != NULL; cur = cur->next) {
68
            i += rcu_dereference_pointer(cur).number;
79
            i += cur->number;
69
        }
80
        }
70
        rcu_read_unlock();
81
        rcu_read_unlock();
71
        thread_usleep(THREADS_SLEEP_LENGTH);
-
 
72
        if (i>RCU_MAX_I || finished)
82
        if (i>RCU_MAX_I*RCU_MAX_I || cfinished>0)
73
        {
83
        {
74
            printf("@");
84
            printf("@");
75
            break;
85
            break;
76
        }
86
        }
-
 
87
        thread_usleep(THREADS_SLEEP_LENGTH);
77
    }
88
    }
78
    finished = true;
89
    cfinished++;
79
}
90
}
80
 
91
 
81
static void writer(void* a)
92
static void writer(void* a)
82
{
93
{
83
    a = (a);
94
    a = (a);
84
    data_t* cur;
95
    data_t* cur;
85
    data_t* newdata, *oldata;
96
    data_t* newdata;
-
 
97
    data_t* oldata;
86
    rcu_callback_list_t* rcudata;
98
    rcu_callback_list_t* rcudata;
87
    int i = 0;
99
    int i = 0;
88
    printf("a1 ");
-
 
89
    rcudata = malloc(sizeof(rcu_callback_list_t),0);
-
 
90
    while (true)
100
    while (true)
91
    {
101
    {
-
 
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
92
        i = rcu_dereference_pointer(first).number;
104
        rcudata = malloc(sizeof(rcu_callback_list_t),0);
93
        rcu_read_lock();
105
        rcu_read_lock();
94
        printf("a2 ");
106
        i = rcu_dereference_pointer(first).number;
95
        for (cur = rcu_dereference_pointer(first).next; cur != first; cur = rcu_dereference_pointer(cur).next) {
107
        for (cur = rcu_dereference_pointer(first).next; cur != NULL; cur = cur->next) {
96
            i += rcu_dereference_pointer(cur).number;
108
            i += cur->number;
97
        }
109
        }
98
        rcu_read_unlock();
110
        rcu_read_unlock();
99
        if (!gquiet)
111
        if (!gquiet)
100
            printf("i%d ",i);
112
            printf("i%d ",i);
101
        if (i>RCU_MAX_I || finished)
113
        if (i>RCU_MAX_I*RCU_MAX_I || cfinished>0)
102
        {
114
        {
103
            printf("!");
115
            printf("!");
104
            break;
116
            break;
105
        }
117
        }
106
        printf("a2b ");
-
 
107
        thread_usleep(THREADS_SLEEP_LENGTH);
-
 
108
 
118
 
109
        printf("a3 ");
119
        //insert a new member 
110
        newdata = malloc(sizeof(data_t),0);
120
        newdata = malloc(sizeof(data_t),0);
111
        printf("a4 ");
121
        newdata->number = (i/(RCU_MAX_I/2))+1;
112
        rcu_read_lock();
122
        rcu_read_lock();
113
        //prepend a new member
123
        //we have to acquire the lock for writing to the structure
114
        spinlock_lock(&write_lock);
124
        spinlock_lock(&write_lock);
115
        printf("a5 ");
-
 
116
        newdata->number = (i/10)+1;
-
 
117
        oldata = first;
-
 
118
        newdata->next = first;
125
        newdata->next = first;
-
 
126
        //rcu_assign_pointer takes care of the necessary write barriers
119
        rcu_assign_pointer(first, newdata);
127
        rcu_assign_pointer(first, newdata);
120
        printf("a6 ");
128
        if (!gquiet)
121
        rcu_sync_callback(&rcu_callback_free, oldata, rcudata);
129
            printf("prepending:%x,n:%d ", newdata, newdata->number);
122
        spinlock_unlock(&write_lock);
130
        spinlock_unlock(&write_lock);
123
        printf("a7 ");
-
 
124
        rcu_read_unlock();
131
        rcu_read_unlock();
125
        thread_usleep(THREADS_SLEEP_LENGTH);
-
 
126
        printf("a8 ");
-
 
127
        if (!gquiet)
-
 
128
            printf(".",i);
-
 
129
 
132
 
-
 
133
 
-
 
134
        //replace a random member
130
        rcu_read_lock();
135
        rcu_read_lock();
-
 
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);
131
        for (cur = rcu_dereference_pointer(first).next; rcu_dereference_pointer(cur).number % 5 != 4 && cur != first; cur = rcu_dereference_pointer(cur).next) {
142
        for (cur = first; cur->next != NULL && cur->next->number >= (i/(RCU_MAX_I/2)); cur = rcu_dereference_pointer(cur).next) {
132
        }
143
        }
133
        if (cur != first){
144
        if (cur->next != NULL) {
134
            //delete the cur->next
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;
135
            spinlock_lock(&write_lock);
148
            newdata->next = cur->next->next;
136
            oldata = rcu_dereference_pointer(cur).next;
149
            oldata = cur->next;
-
 
150
            if (!gquiet)
137
            newdata->next = rcu_dereference_pointer(rcu_dereference_pointer(cur).next).next;
151
                printf("free:%x,n:%d ", cur->next, cur->next->number);
138
            rcu_assign_pointer(rcu_dereference_pointer(cur).next, newdata);
152
            rcu_assign_pointer(cur->next, newdata);
-
 
153
            //free the old member when it is safe (i.e. no references are held)
139
            rcu_sync_callback(&rcu_callback_free, oldata, rcudata);
154
            rcu_sync_callback(&rcu_callback_free, oldata, rcudata);
140
            spinlock_unlock(&write_lock);
155
            spinlock_unlock(&write_lock);
141
            if (!gquiet)
-
 
142
                printf(", ",i);
-
 
143
 
-
 
144
        }
156
        }
145
        rcu_read_unlock();
157
        rcu_read_unlock(); 
146
        thread_usleep(THREADS_SLEEP_LENGTH);
-
 
147
    }
158
    }
148
    finished = true;
159
    cfinished++;
149
}
160
}
150
 
161
 
151
char * test_rcu1(bool quiet)
162
char * test_rcu1(bool quiet)
152
{
163
{
153
    gquiet = quiet;
164
    gquiet = quiet;
154
    data_t* cur, *oldata;
165
    data_t* cur, *oldata;
155
    int i;
166
    int i;
156
    thread_t* t;
167
    thread_t* t;
-
 
168
 
157
    thread_t** threads;
169
    //allocate and initialize the list
158
    first = malloc(sizeof(data_t),0);
170
    first = malloc(sizeof(data_t),0);
159
    first->number = 10;
171
    first->number = 0;
160
    first->next = first;
172
    cur = first;
-
 
173
    for (i=1;i<RCU_MAX_I;i++) {
161
    threads = malloc(sizeof(thread_t*),0);
174
        cur->next = malloc(sizeof(data_t),0);
-
 
175
        cur = cur->next;
-
 
176
        cur->number = i;
-
 
177
    }
162
    finished = false;
178
    cur->next = NULL;
163
 
179
 
-
 
180
    //initialize the counter of finished threads
-
 
181
    cfinished=0;
-
 
182
 
-
 
183
    //start the writers
164
    for(i = 0; i< WRITER_THREADS; i++) {
184
    for(i = 0; i< WRITER_THREADS; i++) {
165
        threads[i]=t=thread_create(writer,NULL, TASK, 0, "writerthread", false );
185
        t=thread_create(writer,NULL, TASK, 0, "writerthread", false );
166
        if (t != NULL)
186
        if (t != NULL)
167
            thread_ready(t);
187
            thread_ready(t);
168
    }
188
    }
169
 
189
 
-
 
190
    //start the readers
170
    for(i = 0; i< READER_THREADS; i++) {
191
    for(i = 0; i< READER_THREADS; i++) {
171
        threads[WRITER_THREADS+i]=t=thread_create(reader,NULL, TASK, 0, "readerthread", false );
192
        t=thread_create(reader,NULL, TASK, 0, "readerthread", false );
172
        if (t != NULL)
193
        if (t != NULL)
173
            thread_ready(t);
194
            thread_ready(t);
174
    }
195
    }
175
 
-
 
-
 
196
    //wait for completion
176
    for (i=0;i<WRITER_THREADS+READER_THREADS;i++)
197
    while (cfinished<WRITER_THREADS+READER_THREADS);
177
        thread_join(threads[i]);
198
    printf("\nfinished all threads!\n");
178
 
-
 
-
 
199
    //free the list
179
    for(cur=first->next;cur->next!=first;) {
200
    for(cur=first->next;cur!=NULL;) {
180
        oldata = cur->next;
201
        oldata = cur->next;
181
        free(cur);
202
        free(cur);
182
        cur = oldata;
203
        cur = oldata;
183
    }
204
    }
184
    free(first);
205
    free(first);