go 파일 저장 스택의 인터페이스 버전

2020/06/24

몇 줄 바꿔봤다. 원래는

푸시/팝에서 형을 지정하지 않고 인터페이스를 썼다. void * 구만?
대충 뮤텍스로 감쌌음..

package main

import (
	"unsafe"
	"os"
	"fmt"
	"syscall"
	"reflect"
//	"flag"
	"golang.org/x/sys/unix"
	"sync"
	"time"
)

var mutex = &sync.Mutex{}
	
const DATA_SIZE	int = 16

type data struct {
//	klen int
	v [DATA_SIZE]byte
}

const INT_SIZE 	int = int(unsafe.Sizeof(int(0)))
const MAX_ITEM	int = 1000000
const ITEM_SIZE int = int(unsafe.Sizeof(data{}))
const FILE_SIZE	int = ITEM_SIZE * MAX_ITEM

type data_file struct {
	pos1 int
	dummy1 [4096 - INT_SIZE]byte
	items [MAX_ITEM]data
	dummy3 [4096 - (MAX_ITEM * ITEM_SIZE) % 4096]byte
	pos2 int
	dummy2 [4096 - INT_SIZE]byte	
}


func Open_data(init bool, filename string) []data_file {

	size := int(unsafe.Sizeof( data_file{} ))
	println(size)

	var map_file *os.File
	var err error

	if init {
	
		map_file, err = os.Create(filename)
	
	} else {
		map_file, err = os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_SYNC, 0755)
	}
	
	defer map_file.Close()
	
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    _, err = map_file.Seek(int64(size - 1), 0)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    
    _, err = map_file.Write([]byte{0})
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
        
    mmap, err := syscall.Mmap(int(map_file.Fd()), 0, int(size), syscall.PROT_READ | syscall.PROT_WRITE, syscall.MAP_SHARED)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    
    if init {
		for i := range mmap {
			mmap[i] = 0
		}
	}

	header := (*reflect.SliceHeader)(unsafe.Pointer(&mmap))
	header.Len = 1
    header.Cap = 1

    map_data := *(*[]data_file)(unsafe.Pointer(header))
	
	err = map_file.Close()    

    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }    
    
    return map_data
}


func Close_data(mmap []data_file) {
	
	header := (*reflect.SliceHeader)(unsafe.Pointer(&mmap))
	header.Len = int(unsafe.Sizeof(data_file{}))
    header.Cap = header.Len	
	
	unmap := *(*[]byte)(unsafe.Pointer(&mmap))
	err := syscall.Munmap(unmap)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
}

var sp int = 0
var gpos1 *int
var gpos2 *int
var max_pos *int


//func push(item_data []byte, store []data_file ) {
func push(item_data interface{}, store []data_file ) {

	var item_byte []byte

	switch item_data.(type) {
		case []byte:
			item_byte = item_data.([]byte)
		case string:
			item_byte = []byte(item_data.(string))
		default:
			
	}
        
//	item_byte := item_data.([]byte)
	item_byte = append(item_byte, 0)
	
	mutex.Lock()
		
	if sp == MAX_ITEM {
		fmt.Fprintf(os.Stderr, "용량 초과, 마지막 푸시 = %s\n", string(item_byte))
		return
	}
	


	copy(store[0].items[*gpos1].v[:], item_byte)
	*gpos1++
	*gpos2++
	sp++
	
	mutex.Unlock()
	
	
}

func pop(store []data_file ) (ret interface{}) {

	mutex.Lock()
	
	sp--
	
	if sp < 0 {
//		ret = []byte("")
		ret = nil
		fmt.Fprintf(os.Stderr, "스택이 비었음\n")
		sp = 0
		return 
	}
	
	*gpos1--
	*gpos2--	
	
	
	ret = store[0].items[*gpos1].v[:]
	
	mutex.Unlock()
	
	return 
}

func main() {

	println(FILE_SIZE)
	
	
//	file := flag.String("file", "stack.dat", "스택 저장소 파일 이름")
//	max_items := flag.Int("max-item", MAX_ITEM, "최대 아이템 갯수")
//	bucket_size := flag.Int("bucket-size", DATA_SIZE, "버킷 크기")

	my_data := Open_data(true, "my_data.dat")

	
	header := (*reflect.SliceHeader)(unsafe.Pointer(&my_data[0].pos1))
	header.Len = 4096
	header.Cap = 4096

	unix.Msync(*(*[]byte)(unsafe.Pointer(header)), unix.MS_SYNC)
	
	header = (*reflect.SliceHeader)(unsafe.Pointer(&my_data[0].pos2))
	header.Len = 4096
    header.Cap = 4096

	unix.Msync(*(*[]byte)(unsafe.Pointer(header)), unix.MS_SYNC)	

	gpos1 = &my_data[0].pos1
	gpos2 = &my_data[0].pos2
	sp = *gpos1
	
	for i := 0; i < 10000; i++ {
		go push(fmt.Sprintf("-- %d --", i), my_data)
	}
	
	time.Sleep(time.Duration(1) * time.Second)
	
	for i := 0; i < 10000; i++ {
		go func() {
			a := pop(my_data)
			if a != nil {
				println(string(a.([]byte)))
			}
		} ()
	}
	
	time.Sleep(time.Duration(4) * time.Second)
	
//	println(string(pop(my_data).([]byte)))
	
	os.Exit(0)
	
	
//	println(string(pop(my_data).([]byte)))
	
//	os.Exit(0)

	push("가나", my_data)
	println(string(pop(my_data).([]byte)))
	
	push([]byte("가나--"), my_data)
	println(string(pop(my_data).([]byte)))
	
//	push(다라"), my_data)
	push([]byte("마바"), my_data)
	push([]byte("하하"), my_data)
	push([]byte("호호"), my_data)
	
	
	println(string(pop(my_data).([]byte)))
	println(string(pop(my_data).([]byte)))
	println()
	
	
	push([]byte("추가1"), my_data)
	push([]byte("추가2"), my_data)
	
	println(string(pop(my_data).([]byte)))
	

/*	
	for {
	
		a := pop(my_data)
		
		if a != nil {
			println(string(a))
		} else {
			break
		}
	}
*/	
	defer Close_data(my_data)
	

}