ronantakizawa/codereview-bench huggingface dataset:
Repository: DiceDB/dice
File: core/eval.go (Go)

Code before:
	key := args[0]

	obj := store.Get(key)

	var hashMap HashMap
	var numKeys int64

	if obj != nil {
		switch currentVal := obj.Value.(type) {
		case HashMap:
			hashMap = currentVal
		default:
			return diceerrors.NewErrWithMessage(diceerrors.WrongTypeErr)
		}
	}

	keyValuePairs := args[1:]
	hashMap, numKeys, err := hashMapBuilder(keyValuePairs, hashMap)
	if err != nil {
		return diceerrors.NewErrWithMessage(err.Error())
	}

	hValue := string(Encode(hashMap, false))
	oType, oEnc := deduceTypeEncoding(hValue)

	obj = store.NewObj(hashMap, -1, oType, oEnc)
	store.Put(key, obj)

	return Encode(numKeys, false)
}

func evalObjectIdleTime(key string, store *Store) []byte {
	obj := store.GetNoTouch(key)
	if obj == nil {
		return RespNIL
	}

	return Encode(int64(getIdleTime(obj.LastAccessedAt)), true)
}

func evalOBJECT(args []string, store *Store) []byte {
	if len(args) < 2 {
		return diceerrors.NewErrArity("OBJECT")
	}

	subcommand := strings.ToUpper(args[0])
	key := args[1]

	switch subcommand {
	case "IDLETIME":
		return evalObjectIdleTime(key, store)

Review comment (suggestion):
We generate the hValue but don't really store it anywhere? If you are only using it to deduce the type encoding - that is probably not required (and you could directly store the exact type and encoding into the object since you know the object is an hmap)

Code after:
	key := args[0]

	obj := store.Get(key)

	var hashMap HashMap
	var numKeys int64

	if obj != nil {
		switch currentVal := obj.Value.(type) {
		case HashMap:
			hashMap = currentVal
		default:
			return diceerrors.NewErrWithMessage(diceerrors.WrongTypeErr)
		}
	}

	keyValuePairs := args[1:]
	hashMap, numKeys, err := hashMapBuilder(keyValuePairs, hashMap)
	if err != nil {
		return diceerrors.NewErrWithMessage(err.Error())
	}

	obj = store.NewObj(hashMap, -1, ObjTypeHashMap, ObjEncodingHashMap)

	store.Put(key, obj)

	return Encode(numKeys, false)
}

func evalObjectIdleTime(key string, store *Store) []byte {
	obj := store.GetNoTouch(key)
	if obj == nil {
		return RespNIL
	}

	return Encode(int64(getIdleTime(obj.LastAccessedAt)), true)
}

func evalOBJECT(args []string, store *Store) []byte {
	if len(args) < 2 {
		return diceerrors.NewErrArity("OBJECT")
	}

	subcommand := strings.ToUpper(args[0])
	key := args[1]

	switch subcommand {
	case "IDLETIME":
		return evalObjectIdleTime(key, store)
	default:
		return diceerrors.NewErrWithMessage(diceerrors.SyntaxErr)
Repository: DiceDB/dice
File: core/eval.go (Go)

Code before:
	if err != nil {
		if err == ErrDequeEmpty {
			return RespNIL
		}
		panic(fmt.Sprintf("unknown error: %v", err))
	}

	return Encode(x, false)
}

func evalFLUSHDB(args []string, store *Store) []byte {
	log.Info(args)
	if len(args) > 1 {
		return diceerrors.NewErrArity("FLUSHDB")
	}

	flushType := constants.Sync
	if len(args) == 1 {
		flushType = strings.ToUpper(args[0])
	}

	switch flushType {
	case constants.Sync:
		store.ResetStore()
	case constants.Async:
		store.ResetStore()
	default:
		return diceerrors.NewErrWithMessage(diceerrors.SyntaxErr)
	}

	return RespOK
}


Review comment (suggestion):
```suggestion
	case constants.Sync, constants.Async:
		store.ResetStore()
```

Code after:
	obj := store.Get(args[0])
	if obj == nil {
		return RespNIL
	}

	if err := assertType(obj.TypeEncoding, ObjTypeByteList); err != nil {
		return Encode(err, false)
	}

	if err := assertEncoding(obj.TypeEncoding, ObjEncodingDeque); err != nil {
		return Encode(err, false)
	}

	deq := obj.Value.(*Deque)
	x, err := deq.LPop()
	if err != nil {
		if err == ErrDequeEmpty {
			return RespNIL
		}
		panic(fmt.Sprintf("unknown error: %v", err))
	}

	return Encode(x, false)
}

func evalFLUSHDB(args []string, store *Store) []byte {
	log.Info(args)
	if len(args) > 1 {
		return diceerrors.NewErrArity("FLUSHDB")
	}

	flushType := constants.Sync
	if len(args) == 1 {
		flushType = strings.ToUpper(args[0])
	}

        // TODO: Update this method to work with shared-nothing multithreaded implementation
	switch flushType {
	case constants.Sync, constants.Async:
		store.ResetStore()
	default:
		return diceerrors.NewErrWithMessage(diceerrors.SyntaxErr)
	}

	return RespOK
}
Repository: DiceDB/dice
File: core/queueref.go (Go)

Code before:
}

// Returns the length of the queue
func (q *QueueRef) Length(store *Store) int64 {
	q.QueueRefCleanup(store)
	return q.qi.Length
}

// QueueRefCleanup removes expired or deleted keys from the QueueRef to maintain the accuracy of QUEUEREFLEN operations.
// While this process ensures correctness, it can be computationally intensive, especially as
// the queue grows. To mitigate performance overhead, constraints such as limiting the queue size
// to 10,000 entries and the number of queues to 100 are imposed, ensuring that the cleanup
// remains efficient without degrading system performance
func (q *QueueRef) QueueRefCleanup(store *Store) {
	var validElements []int64
	for {
		val, err := q.qi.Remove()
		if err != nil {
			break // Break the loop if the queue is empty
		}

		key := *((*string)(unsafe.Pointer(uintptr(val))))
		obj := store.Get(key)
		if obj != nil {
			validElements = append(validElements, val)
		}
	}

	for _, val := range validElements {
		q.qi.Insert(val)
	}
}


Review comment (refactor):
As we are moving away from unsafe.Pointer in general, it is good to avoid it. Since this is not something introduced in this change and needs a bigger refactoring it may be good to open an issue to track this.

Can you please open an issue to move Stack and Queue and Dequeue from using unsafe pointers?

Code after:
func (q *QueueRef) DeepCopy() *QueueRef {
	return &QueueRef{
		qi: q.qi.DeepCopy(),
	}
}

// Returns the length of the queue
func (q *QueueRef) Length(store *Store) int64 {
	q.QueueRefCleanup(store)
	return q.qi.Length
}

// QueueRefCleanup removes expired or deleted keys from the QueueRef to maintain the accuracy of QUEUEREFLEN operations.
// While this process ensures correctness, it can be computationally intensive, especially as
// the queue grows. To mitigate performance overhead, constraints such as limiting the queue size
// to 10,000 entries and the number of queues to 100 are imposed, ensuring that the cleanup
// remains efficient without degrading system performance
func (q *QueueRef) QueueRefCleanup(store *Store) {
	var validElements []int64
	for {
		val, err := q.qi.Remove()
		if err != nil {
			break // Break the loop if the queue is empty
		}

		key := *((*string)(unsafe.Pointer(uintptr(val))))
		obj := store.Get(key)
		if obj != nil {
			validElements = append(validElements, val)
		}
	}

	for _, val := range validElements {
		q.qi.Insert(val)
	}
}
Repository: DiceDB/dice
File: core/eval.go (Go)

Code before:
	for i := range subCommands {
		subCommand := strings.ToUpper(subCommands[i])

		switch subCommand {
		case constants.NX:
			nxCmd = true
			if prevExpiry != nil {
				shouldSetExpiry = false
			}
		case constants.XX:
			xxCmd = true
			if prevExpiry == nil {
				shouldSetExpiry = false
			}
		case constants.GT:
			gtCmd = true
			if prevExpiry == nil || *prevExpiry > newExpInMilli {
				shouldSetExpiry = false
			}
		case constants.LT:
			ltCmd = true
			if prevExpiry != nil && *prevExpiry < newExpInMilli {
				shouldSetExpiry = false
			}
		default:
			return false, errors.New("ERR Unsupported option " + subCommands[i])
		}
	}

	if (nxCmd && (xxCmd || gtCmd || ltCmd)) || (gtCmd && ltCmd) {
		return false, errors.New("ERR NX and XX, GT or LT options at the same time are not compatible")
	}

	store.setUnixTimeExpiry(obj, int64(newExpiry))
	return shouldSetExpiry, nil
}

func evalHELLO(args []string, store *Store) []byte {
	if len(args) > 1 {
		return diceerrors.NewErrArity("HELLO")
	}

	var response []interface{}
	response = append(response,
		"proto", 2,
		"id", serverID,
		"mode", "standalone",
		"role", "master",
		"modules", []interface{}{})

	return Encode(response, false)

Review comment (suggestion):
Please use the error builder utilities to form the error messages. You can refer to other such usages in the code for guidance.
Same for other instances.

Code after:
	for i := range subCommands {
		subCommand := strings.ToUpper(subCommands[i])

		switch subCommand {
		case constants.NX:
			nxCmd = true
			if prevExpiry != nil {
				shouldSetExpiry = false
			}
		case constants.XX:
			xxCmd = true
			if prevExpiry == nil {
				shouldSetExpiry = false
			}
		case constants.GT:
			gtCmd = true
			if prevExpiry == nil || *prevExpiry > newExpInMilli {
				shouldSetExpiry = false
			}
		case constants.LT:
			ltCmd = true
			if prevExpiry != nil && *prevExpiry < newExpInMilli {
				shouldSetExpiry = false
			}
		default:
			return false, diceerrors.NewErrWithMessage("Unsupported option " + subCommands[i])
		}
	}

	if (nxCmd && (xxCmd || gtCmd || ltCmd)) || (gtCmd && ltCmd) {
		return false, diceerrors.NewErrWithMessage("NX and XX," +
			" GT or LT options at the same time are not compatible")
	}

	store.setUnixTimeExpiry(obj, int64(newExpiry))
	return shouldSetExpiry, nil
}

func evalHELLO(args []string, store *Store) []byte {
	if len(args) > 1 {
		return diceerrors.NewErrArity("HELLO")
	}

	var response []interface{}
	response = append(response,
		"proto", 2,
		"id", serverID,
		"mode", "standalone",
		"role", "master",
		"modules", []interface{}{})
Repository: DiceDB/dice
File: core/eval.go (Go)

Code before:
	// Default path is root if not specified
	path := defaultRootPath
	if len(args) > 1 {
		path = args[1]
	}

	// Retrieve the object from the database
	obj := store.Get(key)
	if obj == nil {
		return diceerrors.NewErrWithMessage("could not perform this operation on a key that doesn't exist")
	}

	err := assertType(obj.TypeEncoding, ObjTypeJSON)
	if err != nil {
		return Encode(err, false)
	}
	err = assertEncoding(obj.TypeEncoding, ObjEncodingJSON)
	if err != nil {
		return Encode(err, false)
	}

	jsonData := obj.Value

	_, err = sonic.Marshal(jsonData)
	if err != nil {
		return diceerrors.NewErrWithMessage("could not serialize result")
	}

	var countClear uint64 = 0
	if len(args) == 1 || path == defaultRootPath {
		if jsonData != struct{}{} {
			// If path is root and len(args) == 1, return it instantly
			newObj := store.NewObj(struct{}{}, -1, ObjTypeJSON, ObjEncodingJSON)
			store.Put(key, newObj)
			countClear++
			return Encode(countClear, false)
		}
	}

	expr, err := jp.ParseString(path)
	if err != nil {
		return diceerrors.NewErrWithMessage("invalid JSONPath")
	}

	_, err = expr.Modify(jsonData, func(element any) (altered any, changed bool) {
		switch utils.GetJSONFieldType(element) {
		case constants.IntegerType, constants.NumberType:
			if element != constants.NumberZeroValue {
				countClear++
				return constants.NumberZeroValue, true
			}

Review comment (bug):
To be consistent with Redis, should we change the error message to "Existing key has wrong Redis type"?

```
redis:6379> SET key abc
OK
redis:6379> JSON.CLEAR key $.*
(error) Existing key has wrong Redis type
```

Code after:
	// Default path is root if not specified
	path := defaultRootPath
	if len(args) > 1 {
		path = args[1]
	}

	// Retrieve the object from the database
	obj := store.Get(key)
	if obj == nil {
		return diceerrors.NewErrWithMessage("could not perform this operation on a key that doesn't exist")
	}

	err := assertType(obj.TypeEncoding, ObjTypeJSON)
	if err != nil {
		return diceerrors.NewErrWithMessage("Existing key has wrong Dice type")
	}
	err = assertEncoding(obj.TypeEncoding, ObjEncodingJSON)
	if err != nil {
		return diceerrors.NewErrWithMessage("Existing key has wrong Dice type")
	}

	jsonData := obj.Value

	_, err = sonic.Marshal(jsonData)
	if err != nil {
		return diceerrors.NewErrWithMessage("Existing key has wrong Dice type")
	}

	var countClear uint64 = 0
	if len(args) == 1 || path == defaultRootPath {
		if jsonData != struct{}{} {
			// If path is root and len(args) == 1, return it instantly
			newObj := store.NewObj(struct{}{}, -1, ObjTypeJSON, ObjEncodingJSON)
			store.Put(key, newObj)
			countClear++
			return Encode(countClear, false)
		}
	}

	expr, err := jp.ParseString(path)
	if err != nil {
		return diceerrors.NewErrWithMessage("invalid JSONPath")
	}

	_, err = expr.Modify(jsonData, func(element any) (altered any, changed bool) {
		switch utils.GetJSONFieldType(element) {
		case constants.IntegerType, constants.NumberType:
			if element != constants.NumberZeroValue {
				countClear++
				return constants.NumberZeroValue, true
			}
Repository: DiceDB/dice
File: core/eval.go (Go)

Code before:
// and objects cleared + number of matching JSON numerical values zeroed.
func evalJSONCLEAR(args []string, store *Store) []byte {
	if len(args) < 1 {
		return diceerrors.NewErrArity("JSON.CLEAR")
	}
	key := args[0]

	// Default path is root if not specified
	path := defaultRootPath
	if len(args) > 1 {
		path = args[1]
	}

	// Retrieve the object from the database
	obj := store.Get(key)
	if obj == nil {
		return diceerrors.NewErrWithMessage("could not perform this operation on a key that doesn't exist")
	}

	err := assertType(obj.TypeEncoding, ObjTypeJSON)
	if err != nil {
		return Encode(err, false)
	}
	err = assertEncoding(obj.TypeEncoding, ObjEncodingJSON)
	if err != nil {
		return Encode(err, false)
	}

	jsonData := obj.Value

	_, err = sonic.Marshal(jsonData)
	if err != nil {
		return diceerrors.NewErrWithMessage("could not serialize result")
	}

	var countClear uint64 = 0
	if len(args) == 1 || path == defaultRootPath {
		if jsonData != struct{}{} {
			// If path is root and len(args) == 1, return it instantly
			newObj := store.NewObj(struct{}{}, -1, ObjTypeJSON, ObjEncodingJSON)
			store.Put(key, newObj)
			countClear++
			return Encode(countClear, false)
		}
	}

	expr, err := jp.ParseString(path)
	if err != nil {
		return diceerrors.NewErrWithMessage("invalid JSONPath")
	}


Review comment (bug):
```suggestion
		return diceerrors.NewErrWithMessage("Existing key has wrong Dice type")
```

Code after:
// and objects cleared + number of matching JSON numerical values zeroed.
func evalJSONCLEAR(args []string, store *Store) []byte {
	if len(args) < 1 {
		return diceerrors.NewErrArity("JSON.CLEAR")
	}
	key := args[0]

	// Default path is root if not specified
	path := defaultRootPath
	if len(args) > 1 {
		path = args[1]
	}

	// Retrieve the object from the database
	obj := store.Get(key)
	if obj == nil {
		return diceerrors.NewErrWithMessage("could not perform this operation on a key that doesn't exist")
	}

	err := assertType(obj.TypeEncoding, ObjTypeJSON)
	if err != nil {
		return diceerrors.NewErrWithMessage("Existing key has wrong Dice type")
	}
	err = assertEncoding(obj.TypeEncoding, ObjEncodingJSON)
	if err != nil {
		return diceerrors.NewErrWithMessage("Existing key has wrong Dice type")
	}

	jsonData := obj.Value

	_, err = sonic.Marshal(jsonData)
	if err != nil {
		return diceerrors.NewErrWithMessage("Existing key has wrong Dice type")
	}

	var countClear uint64 = 0
	if len(args) == 1 || path == defaultRootPath {
		if jsonData != struct{}{} {
			// If path is root and len(args) == 1, return it instantly
			newObj := store.NewObj(struct{}{}, -1, ObjTypeJSON, ObjEncodingJSON)
			store.Put(key, newObj)
			countClear++
			return Encode(countClear, false)
		}
	}

	expr, err := jp.ParseString(path)
	if err != nil {
		return diceerrors.NewErrWithMessage("invalid JSONPath")
	}
Repository: DiceDB/dice
File: main.go (Go)

Code before:
	log.Info("Password", config.RequirePass)
}

func main() {
	setupFlags()

	// Handle SIGTERM and SIGINT
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGTERM, syscall.SIGINT)

	// Create a wait group to manage goroutines
	var wg sync.WaitGroup

	// Initialize the AsyncServer
	asyncServer := server.NewAsyncServer(&wg)

	// Find a port and bind it
	if err := asyncServer.FindPortAndBind(); err != nil {
		log.Fatal("Error finding and binding port:", err)
		return
	}

	// Start the server in a goroutine
	wg.Add(1)
	go func() {
		if err := asyncServer.Run(); err != nil {
			log.Fatal("Error running the server:", err)
		}
	}()

	// Start signal handling to listen for shutdown signals in a separate goroutine
	go asyncServer.WaitForSignal(sigs)

	// Wait for all goroutines to complete
	wg.Wait()

	log.Info("Server has shut down gracefully")
}


Review comment (suggestion):
```suggestion
	go func() {
		defer s.wg.Done()
		if err := asyncServer.Run(); err != nil {
```

Code after:

	log.Info("Password", config.RequirePass)
}

func main() {
	setupFlags()

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// Handle SIGTERM and SIGINT
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGTERM, syscall.SIGINT)

	// Initialize the AsyncServer
	asyncServer := server.NewAsyncServer()

	// Find a port and bind it
	if err := asyncServer.FindPortAndBind(); err != nil {
		//nolint: gocritic
		log.Fatal("Error finding and binding port:", err)
	}

	go func() {
		<-sigs
		log.Info("Shutdown signal received")
		cancel()
	}()

	err := asyncServer.Run(ctx)

	// May not be need, just to show we can handle different situations if necessary
	if err != nil {
		if errors.Is(err, context.Canceled) {
			log.Info("Server was canceled")
		} else if errors.Is(err, server.ErrAborted) {
			log.Info("Server received abort command")
		} else {
			log.Error("Server error", "error", err)
		}
	} else {
		log.Info("Server stopped without error")
	}

	// Perform graceful shutdown
	asyncServer.InitiateShutdown()

	log.Info("Server has shut down gracefully")
}
Repository: DiceDB/dice
File: server/async_tcp.go (Go)

Code before:
	}
}

// FindPortAndBind binds the server to the given host and port
func (s *AsyncServer) FindPortAndBind() error {
	serverFD, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, 0)
	if err != nil {
		return err
	}
	s.serverFD = serverFD

	if err := syscall.SetsockoptInt(serverFD, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
		return err
	}

	if err := syscall.SetNonblock(serverFD, true); err != nil {
		return err
	}

	ip4 := net.ParseIP(config.Host)

	return syscall.Bind(serverFD, &syscall.SockaddrInet4{
		Port: config.Port,
		Addr: [4]byte{ip4[0], ip4[1], ip4[2], ip4[3]},
	})
}

// Run starts the server, accepts connections, and handles client requests
func (s *AsyncServer) Run() error {
	defer s.wg.Done()
	defer syscall.Close(s.serverFD)

	if err := s.SetupUsers(); err != nil {
		return err
	}

	s.wg.Add(1)
	go s.WatchKeys()

	if err := syscall.Listen(s.serverFD, s.maxClients); err != nil {
		return err
	}

	var err error
	s.multiplexer, err = iomultiplexer.New(s.maxClients)
	if err != nil {
		return err
	}
	defer s.multiplexer.Close()

	if err := s.multiplexer.Subscribe(iomultiplexer.Event{

Review comment (suggestion):
Shall we handle this condition here for [ParseIP](https://pkg.go.dev/net#ParseIP)?
> If s is not a valid textual representation of an IP address, ParseIP returns nil.

Code after:
// FindPortAndBind binds the server to the given host and port
func (s *AsyncServer) FindPortAndBind() (err error) {
	serverFD, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, 0)
	if err != nil {
		return err
	}
	s.serverFD = serverFD

	// Close the socket on exit if an error occurs
	defer func() {
		if err != nil {
			if err := syscall.Close(serverFD); err != nil {
				log.Warn("failed to close server socket", "error", err)
			}
		}
	}()

	if err := syscall.SetsockoptInt(serverFD, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
		return err
	}

	if err := syscall.SetNonblock(serverFD, true); err != nil {
		return err
	}

	ip4 := net.ParseIP(config.Host)
	if ip4 == nil {
		return ErrInvalidIPAddress
	}

	return syscall.Bind(serverFD, &syscall.SockaddrInet4{
		Port: config.Port,
		Addr: [4]byte{ip4[0], ip4[1], ip4[2], ip4[3]},
	})
}

// Run starts the server, accepts connections, and handles client requests
func (s *AsyncServer) Run(ctx context.Context) error {
	defer func(fd int) {
		err := syscall.Close(fd)
		if err != nil {
			log.Warn("failed to close server socket", "error", err)
		}
	}(s.serverFD)

	watchCtx, cancelWatch := context.WithCancel(ctx)
	defer cancelWatch()

	if err := s.SetupUsers(); err != nil {
		return err
	}
Repository: DiceDB/dice
File: server/async_tcp.go (Go)

Code before:
						log.Error(err)
						return true
					}

					encodedResult := core.Encode(core.CreatePushResponse(&query, &queryResult), false)
					clients.Range(func(clientKey, _ interface{}) bool {
						clientFd := clientKey.(int)
						_, err := syscall.Write(clientFd, encodedResult)
						if err != nil {
							s.store.RemoveWatcher(query, clientFd)
						}
						return true
					})
				}
				return true
			})
		case <-s.ctx.Done():
			return
		}
	}
}

// FindPortAndBind binds the server to the given host and port
func (s *AsyncServer) FindPortAndBind() error {
	serverFD, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, 0)
	if err != nil {
		return err
	}
	s.serverFD = serverFD

	if err := syscall.SetsockoptInt(serverFD, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
		return err
	}

	if err := syscall.SetNonblock(serverFD, true); err != nil {
		return err
	}

	ip4 := net.ParseIP(config.Host)

	return syscall.Bind(serverFD, &syscall.SockaddrInet4{
		Port: config.Port,
		Addr: [4]byte{ip4[0], ip4[1], ip4[2], ip4[3]},
	})
}

// Run starts the server, accepts connections, and handles client requests
func (s *AsyncServer) Run() error {
	defer s.wg.Done()
	defer syscall.Close(s.serverFD)


Review comment (bug):
Shall we defer socket close here in case any of the below operation fails?
```
defer func() {
        if err != nil {
            syscall.Close(serverFD)
        }
    }()
```

Code after:

					encodedResult := core.Encode(core.CreatePushResponse(&query, &queryResult), false)
					clients.Range(func(clientKey, _ interface{}) bool {
						clientFd := clientKey.(int)
						_, err := syscall.Write(clientFd, encodedResult)
						if err != nil {
							s.store.RemoveWatcher(query, clientFd)
						}
						return true
					})
				}
				return true
			})
		case <-ctx.Done():
			return
		}
	}
}

// FindPortAndBind binds the server to the given host and port
func (s *AsyncServer) FindPortAndBind() (err error) {
	serverFD, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, 0)
	if err != nil {
		return err
	}
	s.serverFD = serverFD

	// Close the socket on exit if an error occurs
	defer func() {
		if err != nil {
			if err := syscall.Close(serverFD); err != nil {
				log.Warn("failed to close server socket", "error", err)
			}
		}
	}()

	if err := syscall.SetsockoptInt(serverFD, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
		return err
	}

	if err := syscall.SetNonblock(serverFD, true); err != nil {
		return err
	}

	ip4 := net.ParseIP(config.Host)
	if ip4 == nil {
		return ErrInvalidIPAddress
	}

	return syscall.Bind(serverFD, &syscall.SockaddrInet4{
		Port: config.Port,