Mercurial > ecos
comparison packages/kernel/current/include/mqueue.inl @ 115:6ed91473a1cd ecos-sw-2000-08-21
Merge from eCos master repository on 2000-08-21-22:40:54-BST
| author | jlarmour |
|---|---|
| date | Fri, 25 Aug 2000 17:32:38 +0000 |
| parents | |
| children | f62680ef1804 |
comparison
equal
deleted
inserted
replaced
| 114:5ad2b71d525e | 115:6ed91473a1cd |
|---|---|
| 1 #ifndef CYGONCE_KERNEL_MQUEUE_INL | |
| 2 #define CYGONCE_KERNEL_MQUEUE_INL | |
| 3 /*======================================================================== | |
| 4 // | |
| 5 // mqueue.inl | |
| 6 // | |
| 7 // Message queues implementation | |
| 8 // | |
| 9 //======================================================================== | |
| 10 //####COPYRIGHTBEGIN#### | |
| 11 // | |
| 12 // ------------------------------------------- | |
| 13 // The contents of this file are subject to the Red Hat eCos Public License | |
| 14 // Version 1.1 (the "License"); you may not use this file except in | |
| 15 // compliance with the License. You may obtain a copy of the License at | |
| 16 // http://www.redhat.com/ | |
| 17 // | |
| 18 // Software distributed under the License is distributed on an "AS IS" | |
| 19 // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the | |
| 20 // License for the specific language governing rights and limitations under | |
| 21 // the License. | |
| 22 // | |
| 23 // The Original Code is eCos - Embedded Configurable Operating System, | |
| 24 // released September 30, 1998. | |
| 25 // | |
| 26 // The Initial Developer of the Original Code is Red Hat. | |
| 27 // Portions created by Red Hat are | |
| 28 // Copyright (C) 1998, 1999, 2000 Red Hat, Inc. | |
| 29 // All Rights Reserved. | |
| 30 // ------------------------------------------- | |
| 31 // | |
| 32 //####COPYRIGHTEND#### | |
| 33 //======================================================================== | |
| 34 //#####DESCRIPTIONBEGIN#### | |
| 35 // | |
| 36 // Author(s): jlarmour | |
| 37 // Contributors: | |
| 38 // Date: 2000-05-09 | |
| 39 // Purpose: This file provides the implementation for eCos message | |
| 40 // queues | |
| 41 // Description: This differs from the message boxes also supported | |
| 42 // by eCos primarily because the requirements of message | |
| 43 // queues are driven by POSIX semantics. POSIX semantics are | |
| 44 // more dynamic and therefore heavyweight than Mboxes, | |
| 45 // including prioritization, and variable sized queues and | |
| 46 // message lengths | |
| 47 // Usage: Do not include this file directly - instead | |
| 48 // #include <cyg/kernel/mqueue.hxx> | |
| 49 // | |
| 50 //####DESCRIPTIONEND#### | |
| 51 // | |
| 52 //====================================================================== | |
| 53 */ | |
| 54 | |
| 55 /* CONFIGURATION */ | |
| 56 | |
| 57 #include <pkgconf/system.h> | |
| 58 #include <pkgconf/kernel.h> // Configuration header | |
| 59 | |
| 60 /* INCLUDES */ | |
| 61 | |
| 62 #include <stddef.h> // size_t, NULL | |
| 63 #include <cyg/infra/cyg_type.h> // Types | |
| 64 #include <cyg/kernel/mqueue.hxx> // Header for this file, just in case | |
| 65 #include <cyg/infra/cyg_ass.h> // Assertion support | |
| 66 #include <cyg/infra/cyg_trac.h> // Tracing support | |
| 67 #include <cyg/kernel/sched.hxx> // scheduler | |
| 68 #include <cyg/kernel/sched.inl> // scheduler inlines | |
| 69 #include <cyg/kernel/sema.hxx> // Cyg_Counting_Semaphore | |
| 70 | |
| 71 #ifdef CYGPKG_ISOINFRA | |
| 72 # include <string.h> // memcpy | |
| 73 #else | |
| 74 externC void * memcpy( void *, const void *, size_t ); | |
| 75 #endif | |
| 76 | |
| 77 // NOTE: | |
| 78 // An alternative implementation based on mutexes and condition variables | |
| 79 // rather than semaphores/scheduler locking was considered. But it was | |
| 80 // not thought quite as good because it isn't driver safe. You would | |
| 81 // also have to manage explicitly what counting semaphores do for you | |
| 82 // intrinsically. Also with the mutex approach, the message queue would | |
| 83 // be locked the whole time a new entry was being filled in, or copied out | |
| 84 // | |
| 85 // It also makes the non-blocking case properly non-blocking rather than | |
| 86 // still being able to block while waiting for a mutex protecting | |
| 87 // the message queue internal structures | |
| 88 | |
| 89 /* INLINE FUNCTIONS */ | |
| 90 | |
| 91 //------------------------------------------------------------------------ | |
| 92 | |
| 93 inline cyg_bool | |
| 94 Cyg_Mqueue::check_this( cyg_assert_class_zeal zeal ) const | |
| 95 { | |
| 96 if (zeal != cyg_none) { | |
| 97 CYG_CHECK_DATA_PTRC(this); // extreme paranoia | |
| 98 | |
| 99 #ifdef CYGDBG_USE_ASSERTS | |
| 100 if ( qlen <= 0 || msgsize <= 0 ) | |
| 101 return false; | |
| 102 #endif | |
| 103 | |
| 104 if ( queuespacesize < sizeof(struct qentry)+1 ) | |
| 105 return false; | |
| 106 | |
| 107 CYG_CHECK_DATA_PTRC(queuespace); | |
| 108 CYG_CHECK_FUNC_PTRC(free_fn); | |
| 109 | |
| 110 // prevent pre-emption through this. Not so bad since | |
| 111 // this is only a diagnostic function | |
| 112 Cyg_Scheduler::lock(); | |
| 113 | |
| 114 if (NULL != q) | |
| 115 CYG_CHECK_DATA_PTRC(q); | |
| 116 if (NULL != freelist) | |
| 117 CYG_CHECK_DATA_PTRC(freelist); | |
| 118 if (NULL != callback) | |
| 119 CYG_CHECK_FUNC_PTRC(callback); | |
| 120 | |
| 121 // check each queue entry | |
| 122 long msgs=0, busymsgs=0; | |
| 123 unsigned int oldprio=0; | |
| 124 struct qentry *qtmp; | |
| 125 | |
| 126 if ( NULL != q ) | |
| 127 oldprio = q->priority; | |
| 128 for ( qtmp=q; NULL != qtmp; qtmp=qtmp->next ) { | |
| 129 if ( NULL != qtmp->next ) | |
| 130 CYG_CHECK_DATA_PTRC( qtmp->next ); | |
| 131 | |
| 132 // queue should be priority ordered | |
| 133 if ( qtmp->priority > oldprio ) | |
| 134 goto fail; | |
| 135 oldprio = qtmp->priority; | |
| 136 | |
| 137 #ifdef CYGDBG_USE_ASSERTS | |
| 138 // valid length | |
| 139 if ( !qtmp->busy ) | |
| 140 if ( qtmp->buflen > msgsize ) | |
| 141 goto fail; | |
| 142 #endif | |
| 143 if ( qtmp->busy ) | |
| 144 busymsgs++; | |
| 145 else | |
| 146 msgs++; | |
| 147 } // for | |
| 148 | |
| 149 long freemsgs=0; | |
| 150 | |
| 151 // check that number of used and unused messages == q length | |
| 152 for ( qtmp=freelist; NULL != qtmp; qtmp=qtmp->next ) { | |
| 153 if ( NULL != qtmp->next ) | |
| 154 CYG_CHECK_DATA_PTRC( qtmp->next ); | |
| 155 if ( qtmp->busy ) | |
| 156 busymsgs++; | |
| 157 else | |
| 158 freemsgs++; | |
| 159 } | |
| 160 | |
| 161 #ifdef CYGDBG_USE_ASSERTS | |
| 162 // and sum of all messages should be the total q length | |
| 163 if ( qlen != (msgs+freemsgs+busymsgs) ) | |
| 164 goto fail; | |
| 165 #endif | |
| 166 | |
| 167 Cyg_Scheduler::unlock(); | |
| 168 | |
| 169 } | |
| 170 return true; // object OK | |
| 171 fail: | |
| 172 Cyg_Scheduler::unlock(); | |
| 173 return false; // object fubar'd | |
| 174 } | |
| 175 | |
| 176 //------------------------------------------------------------------------ | |
| 177 | |
| 178 inline | |
| 179 Cyg_Mqueue::Cyg_Mqueue( long maxmsgs, long maxmsgsize, | |
| 180 qalloc_fn_t qalloc, qfree_fn_t qfree, qerr_t *err ) | |
| 181 : putsem(maxmsgs), getsem(0) | |
| 182 { | |
| 183 CYG_REPORT_FUNCTION(); | |
| 184 CYG_REPORT_FUNCARG5( "maxmsgs=%ld, maxmsgsize=%ld, qalloc=%08x, " | |
| 185 "qfree=%08x, &err=%08x", maxmsgs, maxmsgsize, | |
| 186 qalloc, qfree, err); | |
| 187 CYG_PRECONDITIONC( (maxmsgs > 0) && (maxmsgsize > 0) ); | |
| 188 CYG_CHECK_DATA_PTRC( err ); | |
| 189 CYG_CHECK_FUNC_PTRC( qalloc ); | |
| 190 CYG_CHECK_FUNC_PTRC( qfree ); | |
| 191 | |
| 192 // mem to allocate for entire queue size. Also wants to be rounded | |
| 193 // up so that the structs are aligned. | |
| 194 const long addralign = sizeof(void *) - 1; | |
| 195 long entrysize = (sizeof(struct qentry) + maxmsgsize + addralign) | |
| 196 & ~addralign; | |
| 197 | |
| 198 queuespacesize = entrysize * maxmsgs; | |
| 199 queuespace = qalloc( queuespacesize ); | |
| 200 | |
| 201 if (NULL == queuespace) { | |
| 202 *err=NOMEM; | |
| 203 CYG_REPORT_RETURN(); | |
| 204 return; | |
| 205 } | |
| 206 | |
| 207 // link up freelist | |
| 208 long i; | |
| 209 struct qentry *qtmp; | |
| 210 for ( i=0, qtmp=(struct qentry *)queuespace; | |
| 211 i<maxmsgs-1; | |
| 212 i++, qtmp=qtmp->next ) { | |
| 213 qtmp->busy = false; | |
| 214 qtmp->next = (struct qentry *)((char *)qtmp + entrysize); | |
| 215 } // for | |
| 216 | |
| 217 freelist = (struct qentry *)queuespace; | |
| 218 | |
| 219 // set the last entry in the chain to the start to make the list circular | |
| 220 qtmp->next = NULL; | |
| 221 callback = NULL; | |
| 222 q = NULL; | |
| 223 free_fn = qfree; | |
| 224 #ifdef CYGDBG_USE_ASSERTS | |
| 225 qlen = maxmsgs; | |
| 226 msgsize = maxmsgsize; | |
| 227 #endif | |
| 228 | |
| 229 *err = OK; | |
| 230 | |
| 231 // object should be valid now | |
| 232 CYG_ASSERT_THISC(); | |
| 233 | |
| 234 CYG_REPORT_RETURN(); | |
| 235 } | |
| 236 | |
| 237 //------------------------------------------------------------------------ | |
| 238 | |
| 239 inline | |
| 240 Cyg_Mqueue::~Cyg_Mqueue() | |
| 241 { | |
| 242 CYG_REPORT_FUNCTION(); | |
| 243 | |
| 244 if ( NULL != queuespace ) { | |
| 245 // object should be valid if queuespace was successfully allocated | |
| 246 CYG_ASSERT_THISC(); | |
| 247 free_fn( queuespace, queuespacesize ); | |
| 248 } | |
| 249 | |
| 250 #ifdef CYGDBG_USE_ASSERTS | |
| 251 qlen = msgsize = 0; // deliberately make it fail check_this() if used | |
| 252 #endif | |
| 253 | |
| 254 CYG_REPORT_RETURN(); | |
| 255 } | |
| 256 | |
| 257 //------------------------------------------------------------------------ | |
| 258 | |
| 259 // put() copies len bytes of *buf into the queue at priority prio | |
| 260 inline Cyg_Mqueue::qerr_t | |
| 261 Cyg_Mqueue::put( const char *buf, size_t len, unsigned int prio, bool block ) | |
| 262 { | |
| 263 CYG_REPORT_FUNCTYPE( "err=%d"); | |
| 264 CYG_REPORT_FUNCARG4( "buf=%08x, len=%ld, prio=%ud, block=%d", | |
| 265 buf, len, prio, block==true ); | |
| 266 CYG_CHECK_DATA_PTRC( buf ); | |
| 267 CYG_ASSERT_THISC(); | |
| 268 CYG_PRECONDITIONC( len <= (size_t)msgsize ); | |
| 269 | |
| 270 qerr_t err; | |
| 271 struct qentry *qtmp, *qent; | |
| 272 | |
| 273 // wait till a freelist entry is available | |
| 274 if ( true == block ) { | |
| 275 if ( false == putsem.wait() ) { | |
| 276 err = INTR; | |
| 277 goto exit; | |
| 278 } | |
| 279 } else { | |
| 280 if ( false == putsem.trywait() ) { | |
| 281 err = WOULDBLOCK; | |
| 282 goto exit; | |
| 283 } | |
| 284 } | |
| 285 | |
| 286 // prevent preemption when fiddling with important members | |
| 287 Cyg_Scheduler::lock(); | |
| 288 | |
| 289 CYG_ASSERT_THISC(); | |
| 290 | |
| 291 // get a queue entry from the freelist | |
| 292 // don't need to check the freelist - the semaphore tells us there's | |
| 293 // definitely a usable non-busy one there. It's just a question of | |
| 294 // locating it. | |
| 295 | |
| 296 if (!freelist->busy) { // fast-track common case | |
| 297 qent = freelist; | |
| 298 freelist = freelist->next; | |
| 299 } else { | |
| 300 for ( qtmp=freelist; !qtmp->next->busy; qtmp=qtmp->next ) | |
| 301 CYG_EMPTY_STATEMENT; // skip through | |
| 302 qent = qtmp->next; | |
| 303 qtmp->next = qent->next; | |
| 304 } | |
| 305 | |
| 306 // now put it in place in q | |
| 307 | |
| 308 if ( NULL == q ) { | |
| 309 q = qent; | |
| 310 q->next = NULL; | |
| 311 } else { | |
| 312 struct qentry **qentp; | |
| 313 | |
| 314 // insert into queue according to prio | |
| 315 for ( qentp=&q; NULL != *qentp; qentp = &((*qentp)->next) ) { | |
| 316 if ((*qentp)->priority < prio) | |
| 317 break; | |
| 318 } // for | |
| 319 | |
| 320 qent->next = *qentp; | |
| 321 *qentp = qent; | |
| 322 } // else | |
| 323 | |
| 324 qent->priority = prio; // have to set this now so when the sched is | |
| 325 // unlocked, other qent's can be added in the | |
| 326 // right place | |
| 327 qent->busy = true; // let things know this entry should be ignored until | |
| 328 // it's finished having its data copied | |
| 329 | |
| 330 // unlock the scheduler, and potentially switch threads, but | |
| 331 // that's okay now. We don't want it locked for the expensive memcpy | |
| 332 Cyg_Scheduler::unlock(); | |
| 333 | |
| 334 qent->buflen = len; | |
| 335 memcpy( qent->buf(), buf, len ); | |
| 336 | |
| 337 // make available now - setting non-atomically is alright if you think | |
| 338 // about it - the only thing that matters is that it's completed before | |
| 339 // the post() | |
| 340 qent->busy = false; | |
| 341 | |
| 342 // if we have to notify someone, we only do it if no-one's already | |
| 343 // sitting waiting for a message to appear, AND if it's a transition | |
| 344 // from empty to non-empty | |
| 345 | |
| 346 if ( callback != NULL && !getsem.waiting() && (0 == getsem.peek()) ) { | |
| 347 getsem.post(); | |
| 348 callback( *this, callback_data ); | |
| 349 } else | |
| 350 getsem.post(); | |
| 351 | |
| 352 err = OK; | |
| 353 | |
| 354 exit: | |
| 355 CYG_ASSERT_THISC(); | |
| 356 CYG_REPORT_RETVAL(err); | |
| 357 return err; | |
| 358 } // Cyg_Mqueue::put() | |
| 359 | |
| 360 //------------------------------------------------------------------------ | |
| 361 | |
| 362 | |
| 363 // get() returns the oldest highest priority message in the queue in *buf | |
| 364 // and sets *prio to the priority (if prio is non-NULL) and *len to the | |
| 365 // actual message size | |
| 366 | |
| 367 inline Cyg_Mqueue::qerr_t | |
| 368 Cyg_Mqueue::get( char *buf, size_t *len, unsigned int *prio, bool block ) | |
| 369 { | |
| 370 CYG_REPORT_FUNCTYPE( "err=%d"); | |
| 371 CYG_REPORT_FUNCARG4( "buf=%08x, len=%08x, prio=%08x, block=%d", | |
| 372 buf, len, prio, block==true ); | |
| 373 CYG_CHECK_DATA_PTRC( buf ); | |
| 374 CYG_CHECK_DATA_PTRC( len ); | |
| 375 if ( NULL != prio ) | |
| 376 CYG_CHECK_DATA_PTRC( prio ); | |
| 377 CYG_ASSERT_THISC(); | |
| 378 | |
| 379 qerr_t err; | |
| 380 struct qentry *qent; | |
| 381 | |
| 382 // wait till a q entry is available | |
| 383 if ( true == block ) { | |
| 384 if ( false == getsem.wait() ) { | |
| 385 err = INTR; | |
| 386 goto exit; | |
| 387 } | |
| 388 } else { | |
| 389 if ( false == getsem.trywait() ) { | |
| 390 err = WOULDBLOCK; | |
| 391 goto exit; | |
| 392 } | |
| 393 } | |
| 394 | |
| 395 // prevent preemption when fiddling with important members | |
| 396 | |
| 397 Cyg_Scheduler::lock(); | |
| 398 | |
| 399 // don't need to check the q - the semaphore tells us there's | |
| 400 // definitely a usable non-busy one there. It's just a question of | |
| 401 // locating it. | |
| 402 | |
| 403 if ( !q->busy ) { // fast-track the common case | |
| 404 qent = q; | |
| 405 q = qent->next; | |
| 406 } else { | |
| 407 struct qentry *qtmp; | |
| 408 | |
| 409 for ( qtmp=q; !qtmp->next->busy; qtmp=qtmp->next ) | |
| 410 CYG_EMPTY_STATEMENT; // skip through | |
| 411 | |
| 412 qent = qtmp->next; | |
| 413 qtmp->next = qent->next; | |
| 414 } // else | |
| 415 | |
| 416 // now stick at front of freelist, but marked busy | |
| 417 qent->next = freelist; | |
| 418 freelist = qent; | |
| 419 | |
| 420 qent->busy = true; // don't let it truly be part of the freelist just yet | |
| 421 // till the data is copied out | |
| 422 | |
| 423 // unlock the scheduler, and potentially switch threads, but | |
| 424 // that's okay now. We don't want it locked for the expensive memcpy | |
| 425 Cyg_Scheduler::unlock(); | |
| 426 | |
| 427 *len = qent->buflen; | |
| 428 if ( NULL != prio ) | |
| 429 *prio = qent->priority; | |
| 430 memcpy( buf, qent->buf(), *len ); | |
| 431 | |
| 432 // make available now - setting non-atomically is alright if you think | |
| 433 // about it - the only thing that matters is that it's completed before | |
| 434 // the post() | |
| 435 qent->busy = false; | |
| 436 | |
| 437 putsem.post(); | |
| 438 | |
| 439 err = OK; | |
| 440 | |
| 441 exit: | |
| 442 CYG_ASSERT_THISC(); | |
| 443 CYG_REPORT_RETVAL(err); | |
| 444 return err; | |
| 445 | |
| 446 } // Cyg_Mqueue::get() | |
| 447 | |
| 448 //------------------------------------------------------------------------ | |
| 449 | |
| 450 // count() returns the number of messages in the queue | |
| 451 inline long | |
| 452 Cyg_Mqueue::count() | |
| 453 { | |
| 454 CYG_REPORT_FUNCTYPE("curmsgs=%d"); | |
| 455 | |
| 456 long curmsgs = (long)getsem.peek(); | |
| 457 | |
| 458 CYG_REPORT_RETVAL(curmsgs); | |
| 459 return curmsgs; | |
| 460 } // Cyg_Mqueue::count() | |
| 461 | |
| 462 //------------------------------------------------------------------------ | |
| 463 | |
| 464 | |
| 465 // Supply a callback function to call (with the supplied data argument) | |
| 466 // when the queue goes from empty to non-empty (unless someone's already | |
| 467 // doing a get()). This returns the old callback_fn, and if olddata is | |
| 468 // non-NULL sets it to the old data (yes, really!) | |
| 469 inline Cyg_Mqueue::callback_fn_t | |
| 470 Cyg_Mqueue::setnotify( callback_fn_t callback_fn, CYG_ADDRWORD data, | |
| 471 CYG_ADDRWORD *olddata) | |
| 472 { | |
| 473 CYG_REPORT_FUNCTYPE("old callback=%08x"); | |
| 474 CYG_REPORT_FUNCARG3XV( callback_fn, data, olddata ); | |
| 475 if ( NULL != callback_fn ) | |
| 476 CYG_CHECK_FUNC_PTRC( callback_fn ); | |
| 477 if (NULL != olddata) | |
| 478 CYG_CHECK_DATA_PTRC( olddata ); | |
| 479 | |
| 480 callback_fn_t oldfn; | |
| 481 | |
| 482 // Need to prevent preemption for accessing common structures | |
| 483 // Just locking the scheduler has the least overhead | |
| 484 Cyg_Scheduler::lock(); | |
| 485 | |
| 486 oldfn = callback; | |
| 487 if (NULL != olddata) | |
| 488 *olddata = callback_data; | |
| 489 | |
| 490 callback_data = data; | |
| 491 callback = callback_fn; | |
| 492 | |
| 493 Cyg_Scheduler::unlock(); | |
| 494 | |
| 495 CYG_REPORT_RETVAL(oldfn); | |
| 496 return oldfn; | |
| 497 } | |
| 498 | |
| 499 //------------------------------------------------------------------------ | |
| 500 | |
| 501 #endif /* CYGONCE_KERNEL_MQUEUE_INL multiple inclusion protection */ | |
| 502 | |
| 503 /* EOF mqueue.inl */ |
