1
votes

EDIT


I have two modules and both cause bad args errors when fetching from the dictionary(gen_server state)

Here is code from one module

init([ChunkSize, RunningCounter]) ->    
D0 = dict:new(),
D1 = dict:store(chunkSize, ChunkSize, D0),
D2 = dict:store(torrentUploadSpeed, 0, D1),
D3 = dict:store(torrentDownloadSpeed, 0, D2),
TorrentDownloadQueue = queue:new(),
TorrentUploadQueue = queue:new(),
D4 = dict:store(torrentDownloadQueue, TorrentDownloadQueue, D3),
D5 = dict:store(torrentUploadQueue, TorrentUploadQueue, D4),
D6 = dict:store(runningCounter, RunningCounter, D5),
{ok, D6}.

I then set_peer_state which sets up a peer dictionary(1 unique for each peer) The dictionary holds the download and upload (queue and speed) and I add this to the main gen_server state(dictionary) So I have the main torrent data in the main dictionary with a dictionary for each peer stored by the peer id.

set_peer_state(Id) ->
    gen_server:cast(?SERVER, {setPeerState, Id}).

handle_cast({setPeerState, Id}, State) ->
io:format("In the Set Peer State ~p~n", [dict:fetch(runningCounter, State)]),
Id0 = dict:new(), 
PeerDownloadQueue = queue:new(),
PeerUploadQueue = queue:new(),
Id1 = dict:store(peerDownloadQueue, PeerDownloadQueue, Id0),
Id2 = dict:store(peerUploadQueue, PeerUploadQueue, Id1),
Id3 = dict:store(peerDownloadSpeed, 0, Id2),
Id4 = dict:store(peerUploadSpeed, 0, Id3),
D = dict:store(Id, Id4, State),
    {noreply, D};       

This seems to work so far. But when I try updating the torrent state it crashes when fetching from the dictionary.

handle_cast({updateTorrentDownloadState, Time}, State) ->
% fetch the counter for the speed calculation and queue length
RunningCounter  = dict:fetch(runningCounter, State),
% Fetch the Torrents download queue
TorrentDownloadQueue = dict:fetch(torrentDownloadQueue, State),
io:format("The fetched queue is  ~p~n", [dict:fetch(torrentDownloadQueue, State)]),
% Add the item to the queue (main torrent upload queue)
TorrentDownloadQueue2 = queue:in(Time, TorrentDownloadQueue),
% Get the lenght of the downloadQueue
TorrentDownloadQueueLength = queue:len(TorrentDownloadQueue2),
% If the queue is larger than the running counter remove item 
if
    TorrentDownloadQueueLength >= RunningCounter ->
        % Remove item from the queue
        TorrentDownloadQueue3 = queue:drop(TorrentDownloadQueue2),
        update_torrent_download(TorrentDownloadQueue3, State);

    TorrentDownloadQueueLength < RunningCounter ->
        update_torrent_download(TorrentDownloadQueue2, State)
    end;            

and here are the 2 internal functions

update_torrent_download(TorrentDownloadQueue, State) ->
    % Store the queue to the new torrent dict
    State2  = dict:store(torrentDownLoadQueue, TorrentDownloadQueue, State),
    Speed = calculate_speed(TorrentDownloadQueue, State2),
    State3 = dict:store(torrentDownloadSpeed, Speed, State2),
        {noreply, State3}.

calculate_speed(Queue, State) ->
List = queue:to_list(Queue),
Sum = lists:sum(List),
Count = queue:len(Queue),
ChunkSize = dict:fetch(chunkSize, State),
Speed = (Count * ChunkSize) div Sum,
    {ok, Speed}.

Could it be that passing incorrect data to the setters crash the server? Or does the state get lost along the way? This way of doing it seems messy with all the new dicts to store in the old dict, is there a better way to handle this data structure(main torrent and data for each peer)?

I know I could make the dictionaries from lists, but it was messing with my mind at the point I was testing this module.

Thanks

2

2 Answers

5
votes

Your problem is that State is not a dict.

1> dict:fetch(runningCounter, not_a_dict).
** exception error: {badrecord,dict}
     in function  dict:get_slot/2
     in call from dict:fetch/2
1
votes

As YOUR ARGUMENT IS VALID suggested, you're state, at that point of your code, is not a dict.

Answering to your comments, now.

The state of your gen_server is set up in the init function, where you return: {ok, State}.

Every time your gen_server receive a message, an handle_call or an handle_cast are called (depending if the call is synchronous or asynchronous). Inside these functions, the State that you set up during the init phase can be updated and transformed into ANYTHING. You can't rely on the assumption that the "type" of your initial state is the same during the whole execution of your server.

In other words, if you do something like:

init(_Args) -> {ok, dict:new()}.

handle_call(_Request, _From, State) ->
  {ok, completely_new_state}.

you've just "converted" your state from a dict into an atom and that (the atom) is what you will get in subsequent calls.

For this kind of errors, the Erlang tracing tool dbg is quite helpful, allowing you to see how functions are called and which results are returned. Have a look to this short tutorial to learn how to use it:

http://aloiroberto.wordpress.com/2009/02/23/tracing-erlang-functions/

UPDATE:

What you should do:

init(_Args) -> {ok, dict:new()}.

handle_call(_Request, _From, State) ->
  NewState = edit_dict(State),
  {ok, NewState}.

Where the edit_dict function is a function which takes a dict and returns an updated dict.