help with critical regions, please...


    Sep 09 2006 | 9:30 am
    hello,
    I wrote a circular buffer external, and I'm not sure if I need to do anything to make it thread safe.
    The class has a buffer~ that it writes signal input to with the perform function. The buffer is implemented such that it loops around back to the beginning when it reaches the end of the buffer.
    In response to a write message, the class copies the buffer to another buffer (so I can 'un-circle' the buffer and save it to a file).
    Do I need to implement some type of lockout on the buffer while I am copying it so that the perform function isn't accessing the buffer while I'm copying it? Or is this taken care of for me by max?
    for anyone willing to look through my code, it's listed below. I'm worried about the perform method stepping on the write method . The write method is the last method in the code.
    any advice would be appreciated.
    thanks - Andrew
    fyi - the code seems to work fine: compiles, runs, and does what it's supposed to
    /* cbuffer~.c todo - check on critical region of buffer copying while streaming - add file chooser dialog if no file name specified - be able to use external save buffer */
    #include "ext.h" #include "ext_common.h" // contains CLIP macro #include "z_dsp.h" #include "buffer.h" // this defines our buffer's data structure and other goodies
    #define str_eq(s1,s2) (!strcmp ((s1),(s2)))
    void *cbuffer_class;
    // Data structure for class typedef struct _cbuffer { t_pxobject l_obj; t_buffer *l_circular_buffer; // pointer to circular buffer instance t_buffer *l_save_buffer; // pointer to save buffer instance long l_chan; long cBufferIndex; // circular buffer index (in sample frames) long bufferSize; } t_cbuffer;
    // perform method - records audio to circular buffer t_int *cbuffer_perform(t_int *w);
    // dsp method - inserts perform method in DSP chain void cbuffer_dsp(t_cbuffer *x, t_signal **sp);
    // sets both buffers int cbuffer_createInternalBuffers(t_cbuffer *x);
    // new method void *cbuffer_new(long n);
    // saves the circular buffer to a file void cbuffer_write(t_cbuffer *x, t_symbol *filename, t_symbol *filetype);
    //void cbuffer_assist(t_cbuffer *x, void *b, long m, long a, char *s); void cbuffer_dblclick(t_cbuffer *x);
    t_symbol *ps_buffer;
    //****************************************************************************************** // Setup & Initialization Methods
    void main(void) { setup((t_messlist **)&cbuffer_class, (method)cbuffer_new, (method)dsp_free, (short)sizeof(t_cbuffer), 0L, A_LONG, 0);
    // register the inputs and message methods addmess((method)cbuffer_dsp, "dsp", A_CANT, 0); addmess((method)cbuffer_write, "write", A_SYM, A_SYM, 0); //addmess((method)cbuffer_assist, "assist", A_CANT, 0); addmess((method)cbuffer_dblclick, "dblclick", A_CANT, 0);
    // required call dsp_initclass();
    // get the t_symbol for the buffer object ps_buffer = gensym("buffer~"); }
    void cbuffer_dsp(t_cbuffer *x, t_signal **sp) { dsp_add(cbuffer_perform, 4, x, sp[0]->s_vec-1, sp[1]->s_vec-1, sp[0]->s_n+1); }
    void *cbuffer_new(long n) {
    post("creating a cbuffer...");
    // create a new instance of the external class t_cbuffer *x = (t_cbuffer *)newobject(cbuffer_class);
    // reality check if (n { error("cbuffer~: buffer size return(0); }
    // set buffer x->bufferSize = n;
    // set number of channels x->l_chan = 2;
    // call dsp_setup to insert perform function into DSP chain dsp_setup((t_pxobject *)x, 2);
    // set buffers if ( cbuffer_createInternalBuffers(x) == 0 ) return 0;
    // initialize circular buffer index to 0 x->cBufferIndex = 0;
    return (x); }
    //******************************************************************************************
    //****************************************************************************************** // Perform Method
    t_int *cbuffer_perform(t_int *w) { t_cbuffer *x = (t_cbuffer *)(w[1]); // pointer to instance t_float *in1 = (t_float *)(w[2]); // input t_float *in2 = (t_float *)(w[3]); // output int n = (int)(w[4]); // number of sample frames to process per call to perform t_buffer *b = x->l_circular_buffer; // get a reference to the buffer
    float *tab; long chan,frames,nc; long saveinuse;
    // if object is disabled, skip processing if (x->l_obj.z_disabled) goto out;
    // if buffer doesn't exist, output zero if (!b) goto out;
    // if buffer is invalid, output zero if (!b->b_valid) goto out;
    saveinuse = b->b_inuse; // saves the buffers inuse status for reset when done b->b_inuse = true; // set inuse to true
    tab = b->b_samples; // pointer to sample data chan = x->l_chan; // number of channels frames = b->b_frames; // number of sample frames nc = b->b_nchans; // number of channels
    // process all of the samples in the input buffer while (--n) {
    // copy channel1 to buffer tab[x->cBufferIndex] = *++in1;
    // copy channel2 to buffer tab[x->cBufferIndex+1] = *++in2;
    // advance buffer index one frame x->cBufferIndex = x->cBufferIndex + 2;
    // restart frame if necessary if ( x->cBufferIndex >= b->b_size ) x->cBufferIndex = 0;
    } b->b_inuse = saveinuse; return w + 5;
    out: return w + 5; }
    //******************************************************************************************
    //****************************************************************************************** // Create internal buffers
    // creates both buffers int cbuffer_createInternalBuffers(t_cbuffer *x) {
    t_atom bufferArgs[3];
    bufferArgs[0].a_type = A_LONG; bufferArgs[1].a_w.w_long = x->bufferSize; bufferArgs[2].a_type = A_LONG; bufferArgs[2].a_w.w_long = x->bufferSize; bufferArgs[2].a_type = A_LONG; bufferArgs[2].a_w.w_long = 2;
    x->l_circular_buffer = (t_buffer*)newinstance(gensym("buffer~"), 3, bufferArgs); if (x->l_circular_buffer == 0) { error("cbuffer~ : failed to create internal circular buffer~"); return(0); }
    x->l_save_buffer = (t_buffer*)newinstance(gensym("buffer~"), 3, bufferArgs); if (x->l_circular_buffer == 0) { error("cbuffer~ : failed to create internal save buffer~"); return(0); }
    }
    //******************************************************************************************
    //****************************************************************************************** // Other User-defined methods
    // this lets us double-click on cbuffer~ to open up the buffer~ it references void cbuffer_dblclick(t_cbuffer *x) { mess0((t_object *)x->l_circular_buffer,gensym("dblclick")); mess0((t_object *)x->l_save_buffer,gensym("dblclick")); }
    /* void cbuffer_assist(t_cbuffer *x, void *b, long m, long a, char *s) { if (m == ASSIST_OUTLET) sprintf(s,"(signal) Sample Value at Index"); else { switch (a) { case 0: sprintf(s,"(signal) Sample Index"); break; case 1: sprintf(s,"Audio Channel In buffer~"); break; } } } */
    // saves the cbuffer to a file void cbuffer_write(t_cbuffer *x, t_symbol *filename, t_symbol *filetype) {
    post("verifying buffers...");
    t_buffer *cBuffer = x->l_circular_buffer; t_buffer *sBuffer = x->l_save_buffer;
    // make sure buffers are set if (cBuffer == 0) { error("cbuffer~: no circular buffer to save!"); return; } if (sBuffer == 0) { error("cbuffer~: no save buffer to save to!"); return; }
    post("copying buffers...");
    long c_index = x->cBufferIndex; // circular buffer index long s_index = 0; float *c_tab = cBuffer->b_samples; // pointer to cbuffer data float *s_tab = sBuffer->b_samples; // pointer to sbuffer data
    long size = cBuffer->b_size; // buffer size in frames
    //post("c buffer size is %ld", size); //post("s buffer size is %ld", sBuffer->b_size); //post("c buffer index is %ld", c_index);
    while(s_index < size) {
    s_tab[s_index] = c_tab[c_index]; c_index++; s_index++;
    s_tab[s_index] = c_tab[c_index]; c_index++; s_index++;
    // restart circular index at 0 if necessary if ( c_index >= size ) { c_index = 0; }
    }
    post("writing buffer...");
    if (filetype == gensym("aiff")) mess1(x->l_save_buffer,gensym("writeaiff"), filename); else if (filetype == gensym("wav")) mess1(x->l_save_buffer,gensym("writewav"), filename); else { post("trying to save %s, type %s", filename, filetype); error("cbuffer~ : invalid file type for save"); }
    }

    • Sep 11 2006 | 3:22 pm
      Hi Andrew,
      You don't have to worry about reading data from a shared resource - this is safe. If both threads were writing to a shared memory location then you'd have to think about protecting the memory.
      Ben