| /** @file | 
 |  | 
 |   A brief file description | 
 |  | 
 |   @section license License | 
 |  | 
 |   Licensed to the Apache Software Foundation (ASF) under one | 
 |   or more contributor license agreements.  See the NOTICE file | 
 |   distributed with this work for additional information | 
 |   regarding copyright ownership.  The ASF licenses this file | 
 |   to you under the Apache License, Version 2.0 (the | 
 |   "License"); you may not use this file except in compliance | 
 |   with the License.  You may obtain a copy of the License at | 
 |  | 
 |       http://www.apache.org/licenses/LICENSE-2.0 | 
 |  | 
 |   Unless required by applicable law or agreed to in writing, software | 
 |   distributed under the License is distributed on an "AS IS" BASIS, | 
 |   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 |   See the License for the specific language governing permissions and | 
 |   limitations under the License. | 
 |  */ | 
 |  | 
 | #include "ink_unused.h"  /* MAGIC_EDITING_TAG */ | 
 | #include "StateEventLogger.h" | 
 | #include <stdlib.h> | 
 | #include <stdio.h> | 
 | #include <string.h> | 
 | #include <assert.h> | 
 |  | 
 | #ifdef USE_RINGBUF | 
 | /* | 
 |  * two situations: . = data.   d_next== d_free is empty | 
 |  * d_next          d_free                     "A" | 
 |  * v...............v | 
 |  * | 
 |  *       d_free          d_next   v (d_len-1)        "B" | 
 |  * ......v               v......... | 
 |  * d_len points off the end of the array | 
 |  */ | 
 |  | 
 | RingBuf::RingBuf(int len) | 
 | { | 
 |   d_buf = (char *) calloc(1, len + 1); | 
 |   d_len = len + 1; | 
 |   d_free = 0; | 
 |   d_next = 0; | 
 | } | 
 |  | 
 | RingBuf::~RingBuf() | 
 | { | 
 |   xfree(d_buf); | 
 | } | 
 |  | 
 | int | 
 | RingBuf::avail() | 
 | { | 
 |   if (d_next <= d_free) { | 
 |     return d_free - d_next;     // "A" | 
 |   } else { | 
 |     return (d_len - d_next + d_free); | 
 |   } | 
 | } | 
 |  | 
 |                                 // for length 1 array | 
 |                                 // | 
 |                                 // | 
 | int | 
 | RingBuf::free() | 
 | { | 
 |   if (d_next <= d_free) { | 
 |     return (d_next + d_len - d_free - 1);       // "A" | 
 |   } else { | 
 |     return (d_next - d_free - 1); | 
 |   } | 
 | } | 
 |  | 
 | int | 
 | RingBuf::append(char *buf, int len) | 
 | { | 
 |   int towrite = min(len, free()); | 
 |   if (d_next <= d_free) { | 
 |     int wrote = 0; | 
 |     while (d_free<d_len && towrite> 0) { | 
 |       d_buf[d_free] = *buf; | 
 |       buf++; | 
 |       towrite--; | 
 |       d_free++; | 
 |       wrote++; | 
 |     } | 
 |     // fell off end of array or source buf | 
 |     if (d_free == d_len) { | 
 |       d_free = 0; | 
 |     } | 
 |     while (d_free<(d_next - 1) && towrite> 0) { | 
 |       d_buf[d_free] = *buf; | 
 |       buf++; | 
 |       towrite--; | 
 |       d_free++; | 
 |       wrote++; | 
 |     } | 
 |     // we always have a free space | 
 |     assert(d_free != d_next); | 
 |     return wrote; | 
 |   } else { | 
 |     int wrote = 0; | 
 |     while (d_free<d_next && towrite> 0) { | 
 |       d_buf[d_free] = *buf; | 
 |       buf++; | 
 |       towrite--; | 
 |       d_free++; | 
 |       wrote++; | 
 |     } | 
 |     // fell off end of array or source buf | 
 |     // we always have a free space | 
 |     assert(d_free != d_next); | 
 |     return wrote; | 
 |   } | 
 | } | 
 |  | 
 | int | 
 | RingBuf::flush(int d_fd, int len) | 
 | { | 
 |   int toflush = min(len, avail()); | 
 |   int nflushed = 0; | 
 |   if (d_next <= d_free) { | 
 |     if (d_next<d_free && toflush> 0) { | 
 |       int w = min(d_free - d_next, toflush); | 
 |       write(d_fd, &d_buf[d_next], w); | 
 |       nflushed = w; | 
 |       d_next += w; | 
 |       toflush -= w; | 
 |     } | 
 |     assert(toflush == 0); | 
 |     return (nflushed); | 
 |   } else { | 
 |     if (d_next<d_len && toflush> 0) { | 
 |       int w = min(d_len - d_next, toflush); | 
 |       write(d_fd, &d_buf[d_next], w); | 
 |       d_next += w; | 
 |       nflushed = w; | 
 |       toflush -= w; | 
 |     } | 
 |     if (d_next == d_len) { | 
 |       d_next = 0; | 
 |     } | 
 |     if (d_next<d_free && toflush> 0) { | 
 |       int w = min(d_free - d_next, toflush); | 
 |       write(d_fd, &d_buf[d_next], w); | 
 |       d_next += w; | 
 |       nflushed += w; | 
 |       toflush -= w; | 
 |     } | 
 |     assert(toflush == 0); | 
 |     return (nflushed); | 
 |   } | 
 | } | 
 |  | 
 | #ifdef TEST_RINGBUF | 
 | #define test(x) \ | 
 | if (!(x)) { \ | 
 |   printf (# x " failed\n"); \ | 
 | } | 
 |  | 
 | int | 
 | main() | 
 | { | 
 |   RingBuf m(1); | 
 |   char buf[2]; | 
 |   test(m.avail() == 0); | 
 |   test(m.free() == 1); | 
 |   test(m.append("b", 2) == 1); | 
 |   test(m.avail() == 1); | 
 |   test(m.free() == 0); | 
 |   test(m.flush(0, 2) == 1);     // write to stdout | 
 |   //test(buf[0]=='b'); | 
 | } | 
 | #endif | 
 | #endif /* USE_RINGBUF */ | 
 |  | 
 | StateEvent::~StateEvent() | 
 | { | 
 | } | 
 |  | 
 | #ifdef USE_RINGBUF | 
 | void | 
 | StateEventLogger::operator () (const StateEvent & x) | 
 | { | 
 |   if (d_fd < 0) { | 
 |     return; | 
 |   } | 
 |   // try lock d_buf head pointer | 
 |   if (ink_mutex_try_acquire(&d_head_lock)) { | 
 |     // if lock taken and | 
 |     if (d_buf->avail() > d_highwater) { | 
 |       // flush buffer & update head pointer | 
 |       d_buf->flush(d_fd, d_buf->avail()); | 
 |     } | 
 |     // if locked unlock d_buf head pointer | 
 |     ink_mutex_release(&d_head_lock); | 
 |   } | 
 |   // if lock failed, we could be writing to the end of the buffer, | 
 |   // even while another thread is flushing the buffer. | 
 |  | 
 |   // lock d_buf tail pointer | 
 |   if (ink_mutex_acquire(&d_tail_lock)) { | 
 |     if (x.size() < d_buf->free()) { | 
 |       x.marshal(d_buf); | 
 |     } else { | 
 |       // this is bad too. | 
 |       assert(0); | 
 |     } | 
 |     // unlock d_buf tail pointer | 
 |     ink_mutex_release(&d_tail_lock); | 
 |   } else { | 
 |     assert(0);                  // this should never happen | 
 |   } | 
 | } | 
 | #else | 
 |  | 
 | void | 
 | StateEventLogger::operator () (const StateEvent & x) | 
 | { | 
 |   if (d_fd < 0) { | 
 |     return; | 
 |   } | 
 |   char buf[12]; | 
 |   x.marshal(buf); | 
 |   write(d_fd, buf, x.size()); | 
 | } | 
 |  | 
 | #endif | 
 |  | 
 | #ifdef TEST_STATELOGGER | 
 | #define test(x) \ | 
 | if (!(x)) { \ | 
 |   printf (# x " failed\n"); \ | 
 | } | 
 |  | 
 | TestStateEvent x; | 
 | StateEventLogger *sel; | 
 |  | 
 | #define 	N_THREADS 10 | 
 |  | 
 | void * | 
 | test_thread(void *i) | 
 | { | 
 |   int pi = (int) i; | 
 |   int j; | 
 |   int calls = 70000; | 
 |   ink_hrtime start, finish; | 
 |   start = ink_get_hrtime(); | 
 |   for (j = 0; j < calls; j++) { | 
 |     (*sel) (TestStateEvent(pi, j)); | 
 |   } | 
 | #ifdef __alpha | 
 | #define LL_FMT "%ld" | 
 | #else | 
 | #define LL_FMT "%lld" | 
 | #endif | 
 |   finish = ink_get_hrtime(); | 
 |   printf("StateEventLogger cost = " LL_FMT "ns\n", (finish - start) / (N_THREADS * calls)); | 
 |   return NULL; | 
 | } | 
 |  | 
 | int | 
 | main() | 
 | { | 
 |   // create some threads. | 
 |   int i; | 
 |   ink_thread t[N_THREADS]; | 
 |   sel = NEW(new StateEventLogger("sel.out")); | 
 |  | 
 |   for (i = 0; i < N_THREADS; i++) { | 
 |     t[i] = ink_thread_create(test_thread, (void *) i); | 
 |   } | 
 |   for (i = 0; i < N_THREADS; i++) { | 
 |     ink_thread_join(t[i]); | 
 |   } | 
 |   // check that output is not garbled | 
 |   // actually just print out the cost of calling this function: | 
 |   delete sel; | 
 |  | 
 | } | 
 | #endif |