/*
* %CopyrightBegin%
*
* Copyright Ericsson AB 2000-2013. All Rights Reserved.
*
* Licensed 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.
*
* %CopyrightEnd%
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "sys.h"
#include "beam_catches.h"
#include "global.h"
/* R14B04 has about 380 catches when starting erlang */
#define DEFAULT_TABSIZE (1024)
typedef struct {
BeamInstr *cp;
unsigned cdr;
} beam_catch_t;
#ifdef DEBUG
# define IF_DEBUG(x) x
#else
# define IF_DEBUG(x)
#endif
struct bc_pool {
int free_list;
unsigned high_mark;
unsigned tabsize;
beam_catch_t *beam_catches;
/*
* Note that the 'beam_catches' area is shared by pools. Used slots
* are readonly as long as the module is not purgable. The free-list is
* protected by the code_ix lock.
*/
IF_DEBUG(int is_staging;)
};
static struct bc_pool bccix[ERTS_NUM_CODE_IX];
void beam_catches_init(void)
{
int i;
bccix[0].tabsize = DEFAULT_TABSIZE;
bccix[0].free_list = -1;
bccix[0].high_mark = 0;
bccix[0].beam_catches = erts_alloc(ERTS_ALC_T_CODE,
sizeof(beam_catch_t)*DEFAULT_TABSIZE);
IF_DEBUG(bccix[0].is_staging = 0);
for (i=1; i<ERTS_NUM_CODE_IX; i++) {
bccix[i] = bccix[i-1];
}
/* For initial load: */
IF_DEBUG(bccix[erts_staging_code_ix()].is_staging = 1);
}
static void gc_old_vec(beam_catch_t* vec)
{
int i;
for (i=0; i<ERTS_NUM_CODE_IX; i++) {
if (bccix[i].beam_catches == vec) {
return;
}
}
erts_free(ERTS_ALC_T_CODE, vec);
}
void beam_catches_start_staging(void)
{
ErtsCodeIndex dst = erts_staging_code_ix();
ErtsCodeIndex src = erts_active_code_ix();
beam_catch_t* prev_vec = bccix[dst].beam_catches;
ASSERT(!bccix[src].is_staging && !bccix[dst].is_staging);
bccix[dst] = bccix[src];
gc_old_vec(prev_vec);
IF_DEBUG(bccix[dst].is_staging = 1);
}
void beam_catches_end_staging(int commit)
{
IF_DEBUG(bccix[erts_staging_code_ix()].is_staging = 0);
}
unsigned beam_catches_cons(BeamInstr *cp, unsigned cdr)
{
int i;
struct bc_pool* p = &bccix[erts_staging_code_ix()];
ASSERT(p->is_staging);
/*
* Allocate from free_list while it is non-empty.
* If free_list is empty, allocate at high_mark.
*/
if (p->free_list >= 0) {
i = p->free_list;
p->free_list = p->beam_catches[i].cdr;
}
else {
if (p->high_mark >= p->tabsize) {
/* No free slots and table is full: realloc table */
beam_catch_t* prev_vec = p->beam_catches;
unsigned newsize = p->tabsize*2;
p->beam_catches = erts_alloc(ERTS_ALC_T_CODE,
newsize*sizeof(beam_catch_t));
sys_memcpy(p->beam_catches, prev_vec,
p->tabsize*sizeof(beam_catch_t));
gc_old_vec(prev_vec);
p->tabsize = newsize;
}
i = p->high_mark++;
}
p->beam_catches[i].cp = cp;
p->beam_catches[i].cdr = cdr;
return i;
}
BeamInstr *beam_catches_car(unsigned i)
{
struct bc_pool* p = &bccix[erts_active_code_ix()];
if (i >= p->tabsize ) {
erl_exit(1, "beam_catches_delmod: index %#x is out of range\r\n", i);
}
return p->beam_catches[i].cp;
}
void beam_catches_delmod(unsigned head, BeamInstr *code, unsigned code_bytes,
ErtsCodeIndex code_ix)
{
struct bc_pool* p = &bccix[code_ix];
unsigned i, cdr;
ASSERT((code_ix == erts_active_code_ix()) != bccix[erts_staging_code_ix()].is_staging);
for(i = head; i != (unsigned)-1;) {
if (i >= p->tabsize) {
erl_exit(1, "beam_catches_delmod: index %#x is out of range\r\n", i);
}
if( (char*)p->beam_catches[i].cp - (char*)code >= code_bytes ) {
erl_exit(1,
"beam_catches_delmod: item %#x has cp %p which is not "
"in module's range [%p,%p[\r\n",
i, p->beam_catches[i].cp, code, ((char*)code + code_bytes));
}
p->beam_catches[i].cp = 0;
cdr = p->beam_catches[i].cdr;
p->beam_catches[i].cdr = p->free_list;
p->free_list = i;
i = cdr;
}
}